Merge "BatteryService: Allow power supplies to switch type between AC and USB"
diff --git a/Android.mk b/Android.mk
index f45f977..d684cd3 100644
--- a/Android.mk
+++ b/Android.mk
@@ -78,8 +78,9 @@
core/java/android/app/IThumbnailReceiver.aidl \
core/java/android/app/IThumbnailRetriever.aidl \
core/java/android/app/ITransientNotification.aidl \
+ core/java/android/app/IUiAutomationConnection.aidl \
core/java/android/app/IUiModeManager.aidl \
- core/java/android/app/IUserSwitchObserver.aidl \
+ core/java/android/app/IUserSwitchObserver.aidl \
core/java/android/app/IWallpaperManager.aidl \
core/java/android/app/IWallpaperManagerCallback.aidl \
core/java/android/app/admin/IDevicePolicyManager.aidl \
@@ -219,10 +220,7 @@
telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl \
telephony/java/com/android/internal/telephony/IWapPushManager.aidl \
wifi/java/android/net/wifi/IWifiManager.aidl \
- wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \
- voip/java/android/net/sip/ISipSession.aidl \
- voip/java/android/net/sip/ISipSessionListener.aidl \
- voip/java/android/net/sip/ISipService.aidl
+ wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl
#
@@ -353,6 +351,8 @@
../../external/apache-http/src/org/apache/http \
../opt/telephony/src/java/android/telephony \
../opt/telephony/src/java/android/telephony/gsm \
+ ../opt/net/voip/src/java/android/net/rtp \
+ ../opt/net/voip/src/java/android/net/sip
# These are relative to frameworks/base
dirs_to_check_apis := \
@@ -405,6 +405,7 @@
framework \
mms-common \
telephony-common \
+ voip-common \
framework_docs_LOCAL_MODULE_CLASS := JAVA_LIBRARIES
framework_docs_LOCAL_DROIDDOC_HTML_DIR := docs/html
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 06f7c54..2c7d16f 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -149,6 +149,11 @@
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IDisplayMagnificationController.P)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IDisplayMagnificationMediator.java)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IDisplayMagnificationMediator.P)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/voip)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/librtp_jni_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/librtp_jni.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/librtp_jni.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/system/lib/librtp_jni.so)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/api/current.txt b/api/current.txt
index fc60f94..9cd44ed 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2104,6 +2104,7 @@
field public static final int FEEDBACK_SPOKEN = 1; // 0x1
field public static final int FEEDBACK_VISUAL = 8; // 0x8
field public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 2; // 0x2
+ field public static final int FLAG_REPORT_VIEW_IDS = 8; // 0x8
field public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 4; // 0x4
field public int eventTypes;
field public int feedbackType;
@@ -3587,6 +3588,7 @@
method public android.content.ComponentName getComponentName();
method public android.content.Context getContext();
method public android.content.Context getTargetContext();
+ method public android.app.UiAutomation getUiAutomation();
method public boolean invokeContextMenuAction(android.app.Activity, int, int);
method public boolean invokeMenuActionSync(android.app.Activity, int, int);
method public boolean isProfiling();
@@ -4144,6 +4146,26 @@
method public abstract void onTimeSet(android.widget.TimePicker, int, int);
}
+ public final class UiAutomation {
+ method public android.view.accessibility.AccessibilityEvent executeAndWaitForEvent(java.lang.Runnable, com.android.internal.util.Predicate<android.view.accessibility.AccessibilityEvent>, long) throws java.util.concurrent.TimeoutException;
+ method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
+ method public boolean injectInputEvent(android.view.InputEvent, boolean);
+ method public void setOnAccessibilityEventListener(android.app.UiAutomation.OnAccessibilityEventListener);
+ method public boolean setRotation(int);
+ method public android.graphics.Bitmap takeScreenshot();
+ method public void waitForIdle(long, long) throws java.util.concurrent.TimeoutException;
+ field public static final int ROTATION_FREEZE_0 = 0; // 0x0
+ field public static final int ROTATION_FREEZE_180 = 2; // 0x2
+ field public static final int ROTATION_FREEZE_270 = 3; // 0x3
+ field public static final int ROTATION_FREEZE_90 = 1; // 0x1
+ field public static final int ROTATION_FREEZE_CURRENT = -1; // 0xffffffff
+ field public static final int ROTATION_UNFREEZE = -2; // 0xfffffffe
+ }
+
+ public static abstract interface UiAutomation.OnAccessibilityEventListener {
+ method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
+ }
+
public class UiModeManager {
method public void disableCarMode(int);
method public void enableCarMode(int);
@@ -5909,6 +5931,7 @@
field public static final java.lang.String CATEGORY_UNIT_TEST = "android.intent.category.UNIT_TEST";
field public static final android.os.Parcelable.Creator CREATOR;
field public static final java.lang.String EXTRA_ALARM_COUNT = "android.intent.extra.ALARM_COUNT";
+ field public static final java.lang.String EXTRA_ALLOW_MULTIPLE = "android.intent.extra.ALLOW_MULTIPLE";
field public static final deprecated java.lang.String EXTRA_ALLOW_REPLACE = "android.intent.extra.ALLOW_REPLACE";
field public static final java.lang.String EXTRA_ASSIST_CONTEXT = "android.intent.extra.ASSIST_CONTEXT";
field public static final java.lang.String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE";
@@ -20631,21 +20654,23 @@
public class TextToSpeech.Engine {
ctor public TextToSpeech.Engine();
field public static final java.lang.String ACTION_CHECK_TTS_DATA = "android.speech.tts.engine.CHECK_TTS_DATA";
+ field public static final java.lang.String ACTION_GET_SAMPLE_TEXT = "android.speech.tts.engine.GET_SAMPLE_TEXT";
field public static final java.lang.String ACTION_INSTALL_TTS_DATA = "android.speech.tts.engine.INSTALL_TTS_DATA";
field public static final java.lang.String ACTION_TTS_DATA_INSTALLED = "android.speech.tts.engine.TTS_DATA_INSTALLED";
- field public static final int CHECK_VOICE_DATA_BAD_DATA = -1; // 0xffffffff
+ field public static final deprecated int CHECK_VOICE_DATA_BAD_DATA = -1; // 0xffffffff
field public static final int CHECK_VOICE_DATA_FAIL = 0; // 0x0
- field public static final int CHECK_VOICE_DATA_MISSING_DATA = -2; // 0xfffffffe
- field public static final int CHECK_VOICE_DATA_MISSING_VOLUME = -3; // 0xfffffffd
+ field public static final deprecated int CHECK_VOICE_DATA_MISSING_DATA = -2; // 0xfffffffe
+ field public static final deprecated int CHECK_VOICE_DATA_MISSING_VOLUME = -3; // 0xfffffffd
field public static final int CHECK_VOICE_DATA_PASS = 1; // 0x1
field public static final int DEFAULT_STREAM = 3; // 0x3
field public static final java.lang.String EXTRA_AVAILABLE_VOICES = "availableVoices";
- field public static final java.lang.String EXTRA_CHECK_VOICE_DATA_FOR = "checkVoiceDataFor";
- field public static final java.lang.String EXTRA_TTS_DATA_INSTALLED = "dataInstalled";
+ field public static final deprecated java.lang.String EXTRA_CHECK_VOICE_DATA_FOR = "checkVoiceDataFor";
+ field public static final java.lang.String EXTRA_SAMPLE_TEXT = "sampleText";
+ field public static final deprecated java.lang.String EXTRA_TTS_DATA_INSTALLED = "dataInstalled";
field public static final java.lang.String EXTRA_UNAVAILABLE_VOICES = "unavailableVoices";
- field public static final java.lang.String EXTRA_VOICE_DATA_FILES = "dataFiles";
- field public static final java.lang.String EXTRA_VOICE_DATA_FILES_INFO = "dataFilesInfo";
- field public static final java.lang.String EXTRA_VOICE_DATA_ROOT_DIRECTORY = "dataRoot";
+ field public static final deprecated java.lang.String EXTRA_VOICE_DATA_FILES = "dataFiles";
+ field public static final deprecated java.lang.String EXTRA_VOICE_DATA_FILES_INFO = "dataFilesInfo";
+ field public static final deprecated java.lang.String EXTRA_VOICE_DATA_ROOT_DIRECTORY = "dataRoot";
field public static final java.lang.String INTENT_ACTION_TTS_SERVICE = "android.intent.action.TTS_SERVICE";
field public static final java.lang.String KEY_FEATURE_EMBEDDED_SYNTHESIS = "embeddedTts";
field public static final java.lang.String KEY_FEATURE_NETWORK_SYNTHESIS = "networkTts";
@@ -20667,7 +20692,7 @@
method public abstract void onInit(int);
}
- public static abstract interface TextToSpeech.OnUtteranceCompletedListener {
+ public static abstract deprecated interface TextToSpeech.OnUtteranceCompletedListener {
method public abstract void onUtteranceCompleted(java.lang.String);
}
@@ -21308,6 +21333,7 @@
ctor public InstrumentationTestRunner();
method public junit.framework.TestSuite getAllTests();
method protected android.test.AndroidTestRunner getAndroidTestRunner();
+ method public android.os.Bundle getArguments();
method public java.lang.ClassLoader getLoader();
method public junit.framework.TestSuite getTestSuite();
field public static final java.lang.String REPORT_KEY_NAME_CLASS = "class";
@@ -26254,6 +26280,7 @@
method public void addChild(android.view.View, int);
method public int describeContents();
method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(java.lang.String);
+ method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(java.lang.String);
method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
method public android.view.accessibility.AccessibilityNodeInfo focusSearch(int);
method public int getActions();
@@ -26269,6 +26296,7 @@
method public java.lang.CharSequence getPackageName();
method public android.view.accessibility.AccessibilityNodeInfo getParent();
method public java.lang.CharSequence getText();
+ method public java.lang.CharSequence getViewId();
method public int getWindowId();
method public boolean isAccessibilityFocused();
method public boolean isCheckable();
@@ -26316,6 +26344,7 @@
method public void setSource(android.view.View);
method public void setSource(android.view.View, int);
method public void setText(java.lang.CharSequence);
+ method public void setViewId(java.lang.CharSequence);
method public void setVisibleToUser(boolean);
method public void writeToParcel(android.os.Parcel, int);
field public static final int ACTION_ACCESSIBILITY_FOCUS = 64; // 0x40
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index add7a23..b5574cf 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -24,6 +24,7 @@
import android.app.IActivityManager;
import android.app.IInstrumentationWatcher;
import android.app.Instrumentation;
+import android.app.UiAutomationConnection;
import android.content.ComponentName;
import android.content.Context;
import android.content.IIntentReceiver;
@@ -661,10 +662,13 @@
if (cn == null) throw new IllegalArgumentException("Bad component name: " + cnArg);
InstrumentationWatcher watcher = null;
+ UiAutomationConnection connection = null;
if (wait) {
watcher = new InstrumentationWatcher();
watcher.setRawOutput(rawMode);
+ connection = new UiAutomationConnection();
}
+
float[] oldAnims = null;
if (no_window_animation) {
oldAnims = wm.getAnimationScales();
@@ -672,7 +676,7 @@
wm.setAnimationScale(1, 0.0f);
}
- if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, userId)) {
+ if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId)) {
throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
}
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index 2ae2071..c0a1e3b 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -24,6 +24,7 @@
import android.database.Cursor;
import android.net.Uri;
import android.os.Binder;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -102,6 +103,12 @@
+ "equal to \"new_setting\" and sort the result by name in ascending order.\n"
+ " adb shell content query --uri content://settings/secure --projection name:value"
+ " --where \"name=\'new_setting\'\" --sort \"name ASC\"\n"
+ + "\n"
+ + "usage: adb shell content call --uri <URI> --method <METHOD> [--arg <ARG>]\n"
+ + " [--extra <BINDING> ...]\n"
+ + " <METHOD> is the name of a provider-defined method\n"
+ + " <ARG> is an optional string argument\n"
+ + " <BINDING> is like --bind above, typed data of the form <KEY>:{b,s,i,l,f,d}:<VAL>\n"
+ "\n";
private static class Parser {
@@ -109,12 +116,16 @@
private static final String ARGUMENT_DELETE = "delete";
private static final String ARGUMENT_UPDATE = "update";
private static final String ARGUMENT_QUERY = "query";
+ private static final String ARGUMENT_CALL = "call";
private static final String ARGUMENT_WHERE = "--where";
private static final String ARGUMENT_BIND = "--bind";
private static final String ARGUMENT_URI = "--uri";
private static final String ARGUMENT_USER = "--user";
private static final String ARGUMENT_PROJECTION = "--projection";
private static final String ARGUMENT_SORT = "--sort";
+ private static final String ARGUMENT_METHOD = "--method";
+ private static final String ARGUMENT_ARG = "--arg";
+ private static final String ARGUMENT_EXTRA = "--extra";
private static final String TYPE_BOOLEAN = "b";
private static final String TYPE_STRING = "s";
private static final String TYPE_INTEGER = "i";
@@ -141,6 +152,8 @@
return parseUpdateCommand();
} else if (ARGUMENT_QUERY.equals(operation)) {
return parseQueryCommand();
+ } else if (ARGUMENT_CALL.equals(operation)) {
+ return parseCallCommand();
} else {
throw new IllegalArgumentException("Unsupported operation: " + operation);
}
@@ -228,6 +241,38 @@
return new UpdateCommand(uri, userId, values, where);
}
+ public CallCommand parseCallCommand() {
+ String method = null;
+ int userId = UserHandle.USER_OWNER;
+ String arg = null;
+ Uri uri = null;
+ ContentValues values = new ContentValues();
+ for (String argument; (argument = mTokenizer.nextArg())!= null;) {
+ if (ARGUMENT_URI.equals(argument)) {
+ uri = Uri.parse(argumentValueRequired(argument));
+ } else if (ARGUMENT_USER.equals(argument)) {
+ userId = Integer.parseInt(argumentValueRequired(argument));
+ } else if (ARGUMENT_METHOD.equals(argument)) {
+ method = argumentValueRequired(argument);
+ } else if (ARGUMENT_ARG.equals(argument)) {
+ arg = argumentValueRequired(argument);
+ } else if (ARGUMENT_EXTRA.equals(argument)) {
+ parseBindValue(values);
+ } else {
+ throw new IllegalArgumentException("Unsupported argument: " + argument);
+ }
+
+ }
+ if (uri == null) {
+ throw new IllegalArgumentException("Content provider URI not specified."
+ + " Did you specify --uri argument?");
+ }
+ if (method == null) {
+ throw new IllegalArgumentException("Content provider method not specified.");
+ }
+ return new CallCommand(uri, userId, method, arg, values);
+ }
+
public QueryCommand parseQueryCommand() {
Uri uri = null;
int userId = UserHandle.USER_OWNER;
@@ -376,6 +421,43 @@
}
}
+ private static class CallCommand extends Command {
+ final String mMethod, mArg;
+ Bundle mExtras = null;
+
+ public CallCommand(Uri uri, int userId, String method, String arg, ContentValues values) {
+ super(uri, userId);
+ mMethod = method;
+ mArg = arg;
+ if (values != null) {
+ mExtras = new Bundle();
+ for (String key : values.keySet()) {
+ final Object val = values.get(key);
+ if (val instanceof String) {
+ mExtras.putString(key, (String) val);
+ } else if (val instanceof Float) {
+ mExtras.putFloat(key, (Float) val);
+ } else if (val instanceof Double) {
+ mExtras.putDouble(key, (Double) val);
+ } else if (val instanceof Boolean) {
+ mExtras.putBoolean(key, (Boolean) val);
+ } else if (val instanceof Integer) {
+ mExtras.putInt(key, (Integer) val);
+ } else if (val instanceof Long) {
+ mExtras.putLong(key, (Long) val);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onExecute(IContentProvider provider) throws Exception {
+ Bundle result = provider.call(null, mMethod, mArg, mExtras);
+ final int size = result.size(); // unpack
+ System.out.println("Result: " + result);
+ }
+ }
+
private static class QueryCommand extends DeleteCommand {
final String[] mProjection;
final String mSortOrder;
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 7efe189..8dddbc1 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -339,7 +339,10 @@
private static final String LOG_TAG = "AccessibilityService";
- interface Callbacks {
+ /**
+ * @hide
+ */
+ public interface Callbacks {
public void onAccessibilityEvent(AccessibilityEvent event);
public void onInterrupt();
public void onServiceConnected();
@@ -538,8 +541,10 @@
/**
* Implements the internal {@link IAccessibilityServiceClient} interface to convert
* incoming calls to it back to calls on an {@link AccessibilityService}.
+ *
+ * @hide
*/
- static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub
+ public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub
implements HandlerCaller.Callback {
static final int NO_ID = -1;
@@ -610,6 +615,7 @@
mCallback.onServiceConnected();
} else {
AccessibilityInteractionClient.getInstance().removeConnection(connectionId);
+ AccessibilityInteractionClient.getInstance().clearCache();
mCallback.onSetConnectionId(AccessibilityInteractionClient.NO_ID);
}
return;
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 75a4f83..2006bc7 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -33,6 +33,7 @@
import android.util.Xml;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -152,6 +153,15 @@
public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE= 0x0000004;
/**
+ * This flag requests that the {@link AccessibilityNodeInfo}s obtained
+ * by an {@link AccessibilityService} contain the id of the source view.
+ * The source view id will be a fully qualified resource name of the
+ * form "package:id/name", for example "foo.bar:id/my_list", and it is
+ * useful for UI test automation. This flag is not set by default.
+ */
+ public static final int FLAG_REPORT_VIEW_IDS = 0x00000008;
+
+ /**
* The event types an {@link AccessibilityService} is interested in.
* <p>
* <strong>Can be dynamically set at runtime.</strong>
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index f33f503..7a29f35 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -85,15 +85,15 @@
* where to start the search. Use
* {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
* to start from the root.
- * @param id The id of the node.
+ * @param viewId The fully qualified resource name of the view id to find.
* @param interactionId The id of the interaction for matching with the callback result.
* @param callback Callback which to receive the result.
* @param threadId The id of the calling thread.
* @return Whether the call succeeded.
*/
- boolean findAccessibilityNodeInfoByViewId(int accessibilityWindowId, long accessibilityNodeId,
- int viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback,
- long threadId);
+ boolean findAccessibilityNodeInfosByViewId(int accessibilityWindowId,
+ long accessibilityNodeId, String viewId, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, long threadId);
/**
* Finds the {@link android.view.accessibility.AccessibilityNodeInfo} that has the specified
diff --git a/core/java/android/accessibilityservice/UiTestAutomationBridge.java b/core/java/android/accessibilityservice/UiTestAutomationBridge.java
deleted file mode 100644
index 6837386..0000000
--- a/core/java/android/accessibilityservice/UiTestAutomationBridge.java
+++ /dev/null
@@ -1,496 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.accessibilityservice;
-
-import android.accessibilityservice.AccessibilityService.Callbacks;
-import android.accessibilityservice.AccessibilityService.IAccessibilityServiceClientWrapper;
-import android.content.Context;
-import android.os.Bundle;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.util.Log;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityInteractionClient;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.IAccessibilityManager;
-
-import com.android.internal.util.Predicate;
-
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * This class represents a bridge that can be used for UI test
- * automation. It is responsible for connecting to the system,
- * keeping track of the last accessibility event, and exposing
- * window content querying APIs. This class is designed to be
- * used from both an Android application and a Java program
- * run from the shell.
- *
- * @hide
- */
-public class UiTestAutomationBridge {
-
- private static final String LOG_TAG = UiTestAutomationBridge.class.getSimpleName();
-
- private static final int TIMEOUT_REGISTER_SERVICE = 5000;
-
- public static final int ACTIVE_WINDOW_ID = AccessibilityNodeInfo.ACTIVE_WINDOW_ID;
-
- public static final long ROOT_NODE_ID = AccessibilityNodeInfo.ROOT_NODE_ID;
-
- public static final int UNDEFINED = -1;
-
- private static final int FIND_ACCESSIBILITY_NODE_INFO_PREFETCH_FLAGS =
- AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS
- | AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS
- | AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS;
-
- private final Object mLock = new Object();
-
- private volatile int mConnectionId = AccessibilityInteractionClient.NO_ID;
-
- private IAccessibilityServiceClientWrapper mListener;
-
- private AccessibilityEvent mLastEvent;
-
- private volatile boolean mWaitingForEventDelivery;
-
- private volatile boolean mUnprocessedEventAvailable;
-
- private HandlerThread mHandlerThread;
-
- /**
- * Gets the last received {@link AccessibilityEvent}.
- *
- * @return The event.
- */
- public AccessibilityEvent getLastAccessibilityEvent() {
- return mLastEvent;
- }
-
- /**
- * Callback for receiving an {@link AccessibilityEvent}.
- *
- * <strong>Note:</strong> This method is <strong>NOT</strong>
- * executed on the application main thread. The client are
- * responsible for proper synchronization.
- *
- * @param event The received event.
- */
- public void onAccessibilityEvent(AccessibilityEvent event) {
- /* hook - do nothing */
- }
-
- /**
- * Callback for requests to stop feedback.
- *
- * <strong>Note:</strong> This method is <strong>NOT</strong>
- * executed on the application main thread. The client are
- * responsible for proper synchronization.
- */
- public void onInterrupt() {
- /* hook - do nothing */
- }
-
- /**
- * Connects this service.
- *
- * @throws IllegalStateException If already connected.
- */
- public void connect() {
- if (isConnected()) {
- throw new IllegalStateException("Already connected.");
- }
-
- // Serialize binder calls to a handler on a dedicated thread
- // different from the main since we expose APIs that block
- // the main thread waiting for a result the deliver of which
- // on the main thread will prevent that thread from waking up.
- // The serialization is needed also to ensure that events are
- // examined in delivery order. Otherwise, a fair locking
- // is needed for making sure the binder calls are interleaved
- // with check for the expected event and also to make sure the
- // binder threads are allowed to proceed in the received order.
- mHandlerThread = new HandlerThread("UiTestAutomationBridge");
- mHandlerThread.setDaemon(true);
- mHandlerThread.start();
- Looper looper = mHandlerThread.getLooper();
-
- mListener = new IAccessibilityServiceClientWrapper(null, looper, new Callbacks() {
- @Override
- public void onServiceConnected() {
- /* do nothing */
- }
-
- @Override
- public void onInterrupt() {
- UiTestAutomationBridge.this.onInterrupt();
- }
-
- @Override
- public void onAccessibilityEvent(AccessibilityEvent event) {
- synchronized (mLock) {
- while (true) {
- mLastEvent = AccessibilityEvent.obtain(event);
- if (!mWaitingForEventDelivery) {
- mLock.notifyAll();
- break;
- }
- if (!mUnprocessedEventAvailable) {
- mUnprocessedEventAvailable = true;
- mLock.notifyAll();
- break;
- }
- try {
- mLock.wait();
- } catch (InterruptedException ie) {
- /* ignore */
- }
- }
- }
- UiTestAutomationBridge.this.onAccessibilityEvent(event);
- }
-
- @Override
- public void onSetConnectionId(int connectionId) {
- synchronized (mLock) {
- mConnectionId = connectionId;
- mLock.notifyAll();
- }
- }
-
- @Override
- public boolean onGesture(int gestureId) {
- return false;
- }
- });
-
- final IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface(
- ServiceManager.getService(Context.ACCESSIBILITY_SERVICE));
-
- final AccessibilityServiceInfo info = new AccessibilityServiceInfo();
- info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
- info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
- info.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
-
- try {
- manager.registerUiTestAutomationService(mListener, info);
- } catch (RemoteException re) {
- throw new IllegalStateException("Cound not register UiAutomationService.", re);
- }
-
- synchronized (mLock) {
- final long startTimeMillis = SystemClock.uptimeMillis();
- while (true) {
- if (isConnected()) {
- return;
- }
- final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
- final long remainingTimeMillis = TIMEOUT_REGISTER_SERVICE - elapsedTimeMillis;
- if (remainingTimeMillis <= 0) {
- throw new IllegalStateException("Cound not register UiAutomationService.");
- }
- try {
- mLock.wait(remainingTimeMillis);
- } catch (InterruptedException ie) {
- /* ignore */
- }
- }
- }
- }
-
- /**
- * Disconnects this service.
- *
- * @throws IllegalStateException If already disconnected.
- */
- public void disconnect() {
- if (!isConnected()) {
- throw new IllegalStateException("Already disconnected.");
- }
-
- mHandlerThread.quit();
-
- IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface(
- ServiceManager.getService(Context.ACCESSIBILITY_SERVICE));
-
- try {
- manager.unregisterUiTestAutomationService(mListener);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error while unregistering UiTestAutomationService", re);
- }
- }
-
- /**
- * Gets whether this service is connected.
- *
- * @return True if connected.
- */
- public boolean isConnected() {
- return (mConnectionId != AccessibilityInteractionClient.NO_ID);
- }
-
- /**
- * Executes a command and waits for a specific accessibility event type up
- * to a given timeout.
- *
- * @param command The command to execute before starting to wait for the event.
- * @param predicate Predicate for recognizing the awaited event.
- * @param timeoutMillis The max wait time in milliseconds.
- */
- public AccessibilityEvent executeCommandAndWaitForAccessibilityEvent(Runnable command,
- Predicate<AccessibilityEvent> predicate, long timeoutMillis)
- throws TimeoutException, Exception {
- // TODO: This is broken - remove from here when finalizing this as public APIs.
- synchronized (mLock) {
- // Prepare to wait for an event.
- mWaitingForEventDelivery = true;
- mUnprocessedEventAvailable = false;
- if (mLastEvent != null) {
- mLastEvent.recycle();
- mLastEvent = null;
- }
- // Execute the command.
- command.run();
- // Wait for the event.
- final long startTimeMillis = SystemClock.uptimeMillis();
- while (true) {
- // If the expected event is received, that's it.
- if ((mUnprocessedEventAvailable && predicate.apply(mLastEvent))) {
- mWaitingForEventDelivery = false;
- mUnprocessedEventAvailable = false;
- mLock.notifyAll();
- return mLastEvent;
- }
- // Ask for another event.
- mWaitingForEventDelivery = true;
- mUnprocessedEventAvailable = false;
- mLock.notifyAll();
- // Check if timed out and if not wait.
- final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
- final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis;
- if (remainingTimeMillis <= 0) {
- mWaitingForEventDelivery = false;
- mUnprocessedEventAvailable = false;
- mLock.notifyAll();
- throw new TimeoutException("Expacted event not received within: "
- + timeoutMillis + " ms.");
- }
- try {
- mLock.wait(remainingTimeMillis);
- } catch (InterruptedException ie) {
- /* ignore */
- }
- }
- }
- }
-
- /**
- * Waits for the accessibility event stream to become idle, which is not to
- * have received a new accessibility event within <code>idleTimeout</code>,
- * and do so within a maximal global timeout as specified by
- * <code>globalTimeout</code>.
- *
- * @param idleTimeout The timeout between two event to consider the device idle.
- * @param globalTimeout The maximal global timeout in which to wait for idle.
- */
- public void waitForIdle(long idleTimeout, long globalTimeout) {
- final long startTimeMillis = SystemClock.uptimeMillis();
- long lastEventTime = (mLastEvent != null)
- ? mLastEvent.getEventTime() : SystemClock.uptimeMillis();
- synchronized (mLock) {
- while (true) {
- final long currentTimeMillis = SystemClock.uptimeMillis();
- final long sinceLastEventTimeMillis = currentTimeMillis - lastEventTime;
- if (sinceLastEventTimeMillis > idleTimeout) {
- return;
- }
- if (mLastEvent != null) {
- lastEventTime = mLastEvent.getEventTime();
- }
- final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
- final long remainingTimeMillis = globalTimeout - elapsedTimeMillis;
- if (remainingTimeMillis <= 0) {
- return;
- }
- try {
- mLock.wait(idleTimeout);
- } catch (InterruptedException e) {
- /* ignore */
- }
- }
- }
- }
-
- /**
- * Finds an {@link AccessibilityNodeInfo} by accessibility id in the active
- * window. The search is performed from the root node.
- *
- * @param accessibilityNodeId A unique view id or virtual descendant id for
- * which to search.
- * @return The current window scale, where zero means a failure.
- */
- public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityIdInActiveWindow(
- long accessibilityNodeId) {
- return findAccessibilityNodeInfoByAccessibilityId(ACTIVE_WINDOW_ID, accessibilityNodeId);
- }
-
- /**
- * Finds an {@link AccessibilityNodeInfo} by accessibility id.
- *
- * @param accessibilityWindowId A unique window id. Use {@link #ACTIVE_WINDOW_ID} to query
- * the currently active window.
- * @param accessibilityNodeId A unique view id or virtual descendant id for
- * which to search.
- * @return The current window scale, where zero means a failure.
- */
- public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(
- int accessibilityWindowId, long accessibilityNodeId) {
- // Cache the id to avoid locking
- final int connectionId = mConnectionId;
- ensureValidConnection(connectionId);
- return AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
- accessibilityWindowId, accessibilityNodeId,
- FIND_ACCESSIBILITY_NODE_INFO_PREFETCH_FLAGS);
- }
-
- /**
- * Finds an {@link AccessibilityNodeInfo} by View id in the active
- * window. The search is performed from the root node.
- *
- * @param viewId The id of a View.
- * @return The current window scale, where zero means a failure.
- */
- public AccessibilityNodeInfo findAccessibilityNodeInfoByViewIdInActiveWindow(int viewId) {
- return findAccessibilityNodeInfoByViewId(ACTIVE_WINDOW_ID, ROOT_NODE_ID, viewId);
- }
-
- /**
- * Finds an {@link AccessibilityNodeInfo} by View id. The search is performed in
- * the window whose id is specified and starts from the node whose accessibility
- * id is specified.
- *
- * @param accessibilityWindowId A unique window id. Use
- * {@link #ACTIVE_WINDOW_ID} to query the currently active window.
- * @param accessibilityNodeId A unique view id or virtual descendant id from
- * where to start the search. Use {@link #ROOT_NODE_ID} to start from the root.
- * @param viewId The id of a View.
- * @return The current window scale, where zero means a failure.
- */
- public AccessibilityNodeInfo findAccessibilityNodeInfoByViewId(int accessibilityWindowId,
- long accessibilityNodeId, int viewId) {
- // Cache the id to avoid locking
- final int connectionId = mConnectionId;
- ensureValidConnection(connectionId);
- return AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByViewId(connectionId, accessibilityWindowId,
- accessibilityNodeId, viewId);
- }
-
- /**
- * Finds {@link AccessibilityNodeInfo}s by View text in the active
- * window. The search is performed from the root node.
- *
- * @param text The searched text.
- * @return The current window scale, where zero means a failure.
- */
- public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByTextInActiveWindow(String text) {
- return findAccessibilityNodeInfosByText(ACTIVE_WINDOW_ID, ROOT_NODE_ID, text);
- }
-
- /**
- * Finds {@link AccessibilityNodeInfo}s by View text. The match is case
- * insensitive containment. The search is performed in the window whose
- * id is specified and starts from the node whose accessibility id is
- * specified.
- *
- * @param accessibilityWindowId A unique window id. Use
- * {@link #ACTIVE_WINDOW_ID} to query the currently active window.
- * @param accessibilityNodeId A unique view id or virtual descendant id from
- * where to start the search. Use {@link #ROOT_NODE_ID} to start from the root.
- * @param text The searched text.
- * @return The current window scale, where zero means a failure.
- */
- public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(int accessibilityWindowId,
- long accessibilityNodeId, String text) {
- // Cache the id to avoid locking
- final int connectionId = mConnectionId;
- ensureValidConnection(connectionId);
- return AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfosByText(connectionId, accessibilityWindowId,
- accessibilityNodeId, text);
- }
-
- /**
- * Performs an accessibility action on an {@link AccessibilityNodeInfo}
- * in the active window.
- *
- * @param accessibilityNodeId A unique node id (accessibility and virtual descendant id).
- * @param action The action to perform.
- * @param arguments Optional action arguments.
- * @return Whether the action was performed.
- */
- public boolean performAccessibilityActionInActiveWindow(long accessibilityNodeId, int action,
- Bundle arguments) {
- return performAccessibilityAction(ACTIVE_WINDOW_ID, accessibilityNodeId, action, arguments);
- }
-
- /**
- * Performs an accessibility action on an {@link AccessibilityNodeInfo}.
- *
- * @param accessibilityWindowId A unique window id. Use
- * {@link #ACTIVE_WINDOW_ID} to query the currently active window.
- * @param accessibilityNodeId A unique node id (accessibility and virtual descendant id).
- * @param action The action to perform.
- * @param arguments Optional action arguments.
- * @return Whether the action was performed.
- */
- public boolean performAccessibilityAction(int accessibilityWindowId, long accessibilityNodeId,
- int action, Bundle arguments) {
- // Cache the id to avoid locking
- final int connectionId = mConnectionId;
- ensureValidConnection(connectionId);
- return AccessibilityInteractionClient.getInstance().performAccessibilityAction(connectionId,
- accessibilityWindowId, accessibilityNodeId, action, arguments);
- }
-
- /**
- * Gets the root {@link AccessibilityNodeInfo} in the active window.
- *
- * @return The root info.
- */
- public AccessibilityNodeInfo getRootAccessibilityNodeInfoInActiveWindow() {
- // Cache the id to avoid locking
- final int connectionId = mConnectionId;
- ensureValidConnection(connectionId);
- return AccessibilityInteractionClient.getInstance()
- .findAccessibilityNodeInfoByAccessibilityId(connectionId, ACTIVE_WINDOW_ID,
- ROOT_NODE_ID, AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS);
- }
-
- private void ensureValidConnection(int connectionId) {
- if (connectionId == UNDEFINED) {
- throw new IllegalStateException("UiAutomationService not connected."
- + " Did you call #register()?");
- }
- }
-}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index bc27a57d..d8e2239 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -39,7 +39,6 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StrictMode;
-import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.util.Singleton;
@@ -836,8 +835,10 @@
Bundle arguments = data.readBundle();
IBinder b = data.readStrongBinder();
IInstrumentationWatcher w = IInstrumentationWatcher.Stub.asInterface(b);
+ b = data.readStrongBinder();
+ IUiAutomationConnection c = IUiAutomationConnection.Stub.asInterface(b);
int userId = data.readInt();
- boolean res = startInstrumentation(className, profileFile, fl, arguments, w, userId);
+ boolean res = startInstrumentation(className, profileFile, fl, arguments, w, c, userId);
reply.writeNoException();
reply.writeInt(res ? 1 : 0);
return true;
@@ -2874,8 +2875,8 @@
}
public boolean startInstrumentation(ComponentName className, String profileFile,
- int flags, Bundle arguments, IInstrumentationWatcher watcher, int userId)
- throws RemoteException {
+ int flags, Bundle arguments, IInstrumentationWatcher watcher,
+ IUiAutomationConnection connection, int userId) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
@@ -2884,6 +2885,7 @@
data.writeInt(flags);
data.writeBundle(arguments);
data.writeStrongBinder(watcher != null ? watcher.asBinder() : null);
+ data.writeStrongBinder(connection != null ? connection.asBinder() : null);
data.writeInt(userId);
mRemote.transact(START_INSTRUMENTATION_TRANSACTION, data, reply, 0);
reply.readException();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 570fb80..bb73cf4 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -43,7 +43,6 @@
import android.database.sqlite.SQLiteDebug.DbStats;
import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
import android.net.IConnectivityManager;
import android.net.Proxy;
@@ -102,7 +101,6 @@
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.net.InetAddress;
-import java.security.Security;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@@ -420,6 +418,7 @@
ComponentName instrumentationName;
Bundle instrumentationArgs;
IInstrumentationWatcher instrumentationWatcher;
+ IUiAutomationConnection instrumentationUiAutomationConnection;
int debugMode;
boolean enableOpenGlTrace;
boolean restrictedBackupMode;
@@ -730,9 +729,10 @@
ComponentName instrumentationName, String profileFile,
ParcelFileDescriptor profileFd, boolean autoStopProfiler,
Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,
- int debugMode, boolean enableOpenGlTrace, boolean isRestrictedBackupMode,
- boolean persistent, Configuration config, CompatibilityInfo compatInfo,
- Map<String, IBinder> services, Bundle coreSettings) {
+ IUiAutomationConnection instrumentationUiConnection, int debugMode,
+ boolean enableOpenGlTrace, boolean isRestrictedBackupMode, boolean persistent,
+ Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
+ Bundle coreSettings) {
if (services != null) {
// Setup the service cache in the ServiceManager
@@ -748,6 +748,7 @@
data.instrumentationName = instrumentationName;
data.instrumentationArgs = instrumentationArgs;
data.instrumentationWatcher = instrumentationWatcher;
+ data.instrumentationUiAutomationConnection = instrumentationUiConnection;
data.debugMode = debugMode;
data.enableOpenGlTrace = enableOpenGlTrace;
data.restrictedBackupMode = isRestrictedBackupMode;
@@ -4375,7 +4376,8 @@
}
mInstrumentation.init(this, instrContext, appContext,
- new ComponentName(ii.packageName, ii.name), data.instrumentationWatcher);
+ new ComponentName(ii.packageName, ii.name), data.instrumentationWatcher,
+ data.instrumentationUiAutomationConnection);
if (mProfiler.profileFile != null && !ii.handleProfiling
&& mProfiler.profileFd == null) {
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index f0e367c..b1c58f2 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -267,6 +267,9 @@
Bundle testArgs = data.readBundle();
IBinder binder = data.readStrongBinder();
IInstrumentationWatcher testWatcher = IInstrumentationWatcher.Stub.asInterface(binder);
+ binder = data.readStrongBinder();
+ IUiAutomationConnection uiAutomationConnection =
+ IUiAutomationConnection.Stub.asInterface(binder);
int testMode = data.readInt();
boolean openGlTrace = data.readInt() != 0;
boolean restrictedBackupMode = (data.readInt() != 0);
@@ -277,8 +280,9 @@
Bundle coreSettings = data.readBundle();
bindApplication(packageName, info,
providers, testName, profileName, profileFd, autoStopProfiler,
- testArgs, testWatcher, testMode, openGlTrace, restrictedBackupMode,
- persistent, config, compatInfo, services, coreSettings);
+ testArgs, testWatcher, uiAutomationConnection, testMode,
+ openGlTrace, restrictedBackupMode, persistent, config, compatInfo,
+ services, coreSettings);
return true;
}
@@ -874,10 +878,11 @@
public final void bindApplication(String packageName, ApplicationInfo info,
List<ProviderInfo> providers, ComponentName testName, String profileName,
ParcelFileDescriptor profileFd, boolean autoStopProfiler, Bundle testArgs,
- IInstrumentationWatcher testWatcher, int debugMode, boolean openGlTrace,
- boolean restrictedBackupMode, boolean persistent,
- Configuration config, CompatibilityInfo compatInfo,
- Map<String, IBinder> services, Bundle coreSettings) throws RemoteException {
+ IInstrumentationWatcher testWatcher,
+ IUiAutomationConnection uiAutomationConnection, int debugMode,
+ boolean openGlTrace, boolean restrictedBackupMode, boolean persistent,
+ Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
+ Bundle coreSettings) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeString(packageName);
@@ -899,6 +904,7 @@
data.writeInt(autoStopProfiler ? 1 : 0);
data.writeBundle(testArgs);
data.writeStrongInterface(testWatcher);
+ data.writeStrongInterface(uiAutomationConnection);
data.writeInt(debugMode);
data.writeInt(openGlTrace ? 1 : 0);
data.writeInt(restrictedBackupMode ? 1 : 0);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index fd4389e..e03d3fd 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1475,7 +1475,7 @@
arguments.setAllowFds(false);
}
return ActivityManagerNative.getDefault().startInstrumentation(
- className, profileFile, 0, arguments, null, getUserId());
+ className, profileFile, 0, arguments, null, null, getUserId());
} catch (RemoteException e) {
// System has crashed, nothing we can do.
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index baac07f..5a493294 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -158,8 +158,8 @@
public void killApplicationProcess(String processName, int uid) throws RemoteException;
public boolean startInstrumentation(ComponentName className, String profileFile,
- int flags, Bundle arguments, IInstrumentationWatcher watcher, int userId)
- throws RemoteException;
+ int flags, Bundle arguments, IInstrumentationWatcher watcher,
+ IUiAutomationConnection connection, int userId) throws RemoteException;
public void finishInstrumentation(IApplicationThread target,
int resultCode, Bundle results) throws RemoteException;
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 8516694..3189b31a 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -90,7 +90,8 @@
void bindApplication(String packageName, ApplicationInfo info, List<ProviderInfo> providers,
ComponentName testName, String profileName, ParcelFileDescriptor profileFd,
boolean autoStopProfiler, Bundle testArguments, IInstrumentationWatcher testWatcher,
- int debugMode, boolean openGlTrace, boolean restrictedBackupMode, boolean persistent,
+ IUiAutomationConnection uiAutomationConnection, int debugMode,
+ boolean openGlTrace, boolean restrictedBackupMode, boolean persistent,
Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
Bundle coreSettings) throws RemoteException;
void scheduleExit() throws RemoteException;
diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl
new file mode 100644
index 0000000..09bf829
--- /dev/null
+++ b/core/java/android/app/IUiAutomationConnection.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.accessibilityservice.IAccessibilityServiceClient;
+import android.graphics.Bitmap;
+import android.view.InputEvent;
+import android.os.ParcelFileDescriptor;
+
+/**
+ * This interface contains privileged operations a shell program can perform
+ * on behalf of an instrumentation that it runs. These operations require
+ * special permissions which the shell user has but the instrumentation does
+ * not. Running privileged operations by the shell user on behalf of an
+ * instrumentation is needed for running UiTestCases.
+ *
+ * {@hide}
+ */
+interface IUiAutomationConnection {
+ void connect(IAccessibilityServiceClient client);
+ void disconnect();
+ boolean injectInputEvent(in InputEvent event, boolean sync);
+ boolean setRotation(int rotation);
+ Bitmap takeScreenshot(int width, int height);
+ void shutdown();
+}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 39186c6..a2eeddd 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -49,7 +49,6 @@
import java.util.ArrayList;
import java.util.List;
-
/**
* Base class for implementing application instrumentation code. When running
* with instrumentation turned on, this class will be instantiated for you
@@ -59,6 +58,7 @@
* <instrumentation> tag.
*/
public class Instrumentation {
+
/**
* If included in the status or final bundle sent to an IInstrumentationWatcher, this key
* identifies the class that is writing the report. This can be used to provide more structured
@@ -73,7 +73,7 @@
* instrumentation can also be launched, and results collected, by an automated system.
*/
public static final String REPORT_KEY_STREAMRESULT = "stream";
-
+
private static final String TAG = "Instrumentation";
private final Object mSync = new Object();
@@ -86,9 +86,11 @@
private List<ActivityWaiter> mWaitingActivities;
private List<ActivityMonitor> mActivityMonitors;
private IInstrumentationWatcher mWatcher;
+ private IUiAutomationConnection mUiAutomationConnection;
private boolean mAutomaticPerformanceSnapshots = false;
private PerformanceCollector mPerformanceCollector;
private Bundle mPerfMetrics = new Bundle();
+ private UiAutomation mUiAutomation;
public Instrumentation() {
}
@@ -1598,13 +1600,14 @@
/*package*/ final void init(ActivityThread thread,
Context instrContext, Context appContext, ComponentName component,
- IInstrumentationWatcher watcher) {
+ IInstrumentationWatcher watcher, IUiAutomationConnection uiAutomationConnection) {
mThread = thread;
mMessageQueue = mThread.getLooper().myQueue();
mInstrContext = instrContext;
mAppContext = appContext;
mComponent = component;
mWatcher = watcher;
+ mUiAutomationConnection = uiAutomationConnection;
}
/*package*/ static void checkStartActivityResult(int res, Object intent) {
@@ -1644,12 +1647,42 @@
}
}
+ /**
+ * Gets the {@link UiAutomation} instance.
+ * <p>
+ * <strong>Note:</strong> The APIs exposed via the returned {@link UiAutomation}
+ * work across application boundaries while the APIs exposed by the instrumentation
+ * do not. For example, {@link Instrumentation#sendPointerSync(MotionEvent)} will
+ * not allow you to inject the event in an app different from the instrumentation
+ * target, while {@link UiAutomation#injectInputEvent(android.view.InputEvent, boolean)}
+ * will work regardless of the current application.
+ * </p>
+ * <p>
+ * A typical test case should be using either the {@link UiAutomation} or
+ * {@link Instrumentation} APIs. Using both APIs at the same time is not
+ * a mistake by itself but a client has to be aware of the APIs limitations.
+ * </p>
+ * @return The UI automation instance.
+ *
+ * @see UiAutomation
+ */
+ public UiAutomation getUiAutomation() {
+ if (mUiAutomationConnection != null) {
+ if (mUiAutomation == null) {
+ mUiAutomation = new UiAutomation(getTargetContext().getMainLooper(),
+ mUiAutomationConnection);
+ mUiAutomation.connect();
+ }
+ return mUiAutomation;
+ }
+ return null;
+ }
+
private final class InstrumentationThread extends Thread {
public InstrumentationThread(String name) {
super(name);
}
public void run() {
- IActivityManager am = ActivityManagerNative.getDefault();
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
} catch (RuntimeException e) {
@@ -1660,9 +1693,13 @@
startPerformanceSnapshot();
}
onStart();
+ if (mUiAutomation != null) {
+ mUiAutomation.disconnect();
+ mUiAutomation = null;
+ }
}
}
-
+
private static final class EmptyRunnable implements Runnable {
public void run() {
}
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
new file mode 100644
index 0000000..e611f6d
--- /dev/null
+++ b/core/java/android/app/UiAutomation.java
@@ -0,0 +1,597 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.accessibilityservice.AccessibilityService.Callbacks;
+import android.accessibilityservice.AccessibilityService.IAccessibilityServiceClientWrapper;
+import android.accessibilityservice.IAccessibilityServiceClient;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Point;
+import android.hardware.display.DisplayManagerGlobal;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.Display;
+import android.view.InputEvent;
+import android.view.Surface;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityInteractionClient;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.IAccessibilityInteractionConnection;
+
+import com.android.internal.util.Predicate;
+
+import java.util.ArrayList;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Class for interacting with the device's UI by simulation user actions and
+ * introspection of the screen content. It relies on the platform accessibility
+ * APIs to introspect the screen and to perform some actions on the remote view
+ * tree. It also allows injecting of arbitrary raw input events simulating user
+ * interaction with keyboards and touch devices.
+ * <p>
+ * The APIs exposed by this class are low-level to maximize flexibility when
+ * developing UI test automation tools and libraries. Generally, a UiAutomation
+ * client should be using a higher-level library or implement high-level functions.
+ * For example, performing a tap on the screen requires construction and injecting
+ * of a touch down and up events which have to be delivered to the system by a
+ * call to {@link #injectInputEvent(InputEvent, boolean)}.
+ * </p>
+ * <p>
+ * The APIs exposed by this class operate across applications enabling a client
+ * to write tests that cover use cases spanning over multiple applications. For
+ * example, going to the settings application to change a setting and then
+ * interacting with another application whose behavior depends on that setting.
+ * </p>
+ */
+public final class UiAutomation {
+
+ private static final String LOG_TAG = UiAutomation.class.getSimpleName();
+
+ private static final boolean DEBUG = false;
+
+ private static final int CONNECTION_ID_UNDEFINED = -1;
+
+ private static final long CONNECT_TIMEOUT_MILLIS = 5000;
+
+ /** Rotation constant: Unfreeze rotation (rotating the device changes its rotation state). */
+ public static final int ROTATION_UNFREEZE = -2;
+
+ /** Rotation constant: Freeze rotation to its current state. */
+ public static final int ROTATION_FREEZE_CURRENT = -1;
+
+ /** Rotation constant: Freeze rotation to 0 degrees (natural orientation) */
+ public static final int ROTATION_FREEZE_0 = Surface.ROTATION_0;
+
+ /** Rotation constant: Freeze rotation to 90 degrees . */
+ public static final int ROTATION_FREEZE_90 = Surface.ROTATION_90;
+
+ /** Rotation constant: Freeze rotation to 180 degrees . */
+ public static final int ROTATION_FREEZE_180 = Surface.ROTATION_180;
+
+ /** Rotation constant: Freeze rotation to 270 degrees . */
+ public static final int ROTATION_FREEZE_270 = Surface.ROTATION_270;
+
+ private final Object mLock = new Object();
+
+ private final ArrayList<AccessibilityEvent> mEventQueue = new ArrayList<AccessibilityEvent>();
+
+ private final IAccessibilityServiceClient mClient;
+
+ private final IUiAutomationConnection mUiAutomationConnection;
+
+ private int mConnectionId = CONNECTION_ID_UNDEFINED;
+
+ private OnAccessibilityEventListener mOnAccessibilityEventListener;
+
+ private boolean mWaitingForEventDelivery;
+
+ private long mLastEventTimeMillis;
+
+ private boolean mIsConnecting;
+
+ /**
+ * Listener for observing the {@link AccessibilityEvent} stream.
+ */
+ public static interface OnAccessibilityEventListener {
+
+ /**
+ * Callback for receiving an {@link AccessibilityEvent}.
+ * <p>
+ * <strong>Note:</strong> This method is <strong>NOT</strong> executed
+ * on the main test thread. The client is responsible for proper
+ * synchronization.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> It is responsibility of the client
+ * to recycle the received events to minimize object creation.
+ * </p>
+ *
+ * @param event The received event.
+ */
+ public void onAccessibilityEvent(AccessibilityEvent event);
+ }
+
+ /**
+ * Creates a new instance that will handle callbacks from the accessibility
+ * layer on the thread of the provided looper and perform requests for privileged
+ * operations on the provided connection.
+ *
+ * @param looper The looper on which to execute accessibility callbacks.
+ * @param connection The connection for performing privileged operations.
+ *
+ * @hide
+ */
+ public UiAutomation(Looper looper, IUiAutomationConnection connection) {
+ if (looper == null) {
+ throw new IllegalArgumentException("Looper cannot be null!");
+ }
+ if (connection == null) {
+ throw new IllegalArgumentException("Connection cannot be null!");
+ }
+ mUiAutomationConnection = connection;
+ mClient = new IAccessibilityServiceClientImpl(looper);
+ }
+
+ /**
+ * Connects this UiAutomation to the accessibility introspection APIs.
+ *
+ * @hide
+ */
+ public void connect() {
+ synchronized (mLock) {
+ throwIfConnectedLocked();
+ if (mIsConnecting) {
+ return;
+ }
+ mIsConnecting = true;
+ }
+
+ try {
+ // Calling out without a lock held.
+ mUiAutomationConnection.connect(mClient);
+ } catch (RemoteException re) {
+ throw new RuntimeException("Error while connecting UiAutomation", re);
+ }
+
+ synchronized (mLock) {
+ final long startTimeMillis = SystemClock.uptimeMillis();
+ try {
+ while (true) {
+ if (isConnectedLocked()) {
+ break;
+ }
+ final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+ final long remainingTimeMillis = CONNECT_TIMEOUT_MILLIS - elapsedTimeMillis;
+ if (remainingTimeMillis <= 0) {
+ throw new RuntimeException("Error while connecting UiAutomation");
+ }
+ try {
+ mLock.wait(remainingTimeMillis);
+ } catch (InterruptedException ie) {
+ /* ignore */
+ }
+ }
+ } finally {
+ mIsConnecting = false;
+ }
+ }
+ }
+
+ /**
+ * Disconnects this UiAutomation from the accessibility introspection APIs.
+ *
+ * @hide
+ */
+ public void disconnect() {
+ synchronized (mLock) {
+ if (mIsConnecting) {
+ throw new IllegalStateException(
+ "Cannot call disconnect() while connecting!");
+ }
+ throwIfNotConnectedLocked();
+ mConnectionId = CONNECTION_ID_UNDEFINED;
+ }
+ try {
+ // Calling out without a lock held.
+ mUiAutomationConnection.disconnect();
+ } catch (RemoteException re) {
+ throw new RuntimeException("Error while disconnecting UiAutomation", re);
+ }
+ }
+
+ /**
+ * The id of the {@link IAccessibilityInteractionConnection} for querying
+ * the screen content. This is here for legacy purposes since some tools use
+ * hidden APIs to introspect the screen.
+ *
+ * @hide
+ */
+ public int getConnectionId() {
+ synchronized (mLock) {
+ throwIfNotConnectedLocked();
+ return mConnectionId;
+ }
+ }
+
+ /**
+ * Sets a callback for observing the stream of {@link AccessibilityEvent}s.
+ *
+ * @param listener The callback.
+ */
+ public void setOnAccessibilityEventListener(OnAccessibilityEventListener listener) {
+ synchronized (mLock) {
+ mOnAccessibilityEventListener = listener;
+ }
+ }
+
+ /**
+ * Gets the root {@link AccessibilityNodeInfo} in the active window.
+ *
+ * @return The root info.
+ */
+ public AccessibilityNodeInfo getRootInActiveWindow() {
+ final int connectionId;
+ synchronized (mLock) {
+ throwIfNotConnectedLocked();
+ connectionId = mConnectionId;
+ }
+ // Calling out without a lock held.
+ return AccessibilityInteractionClient.getInstance()
+ .getRootInActiveWindow(connectionId);
+ }
+
+ /**
+ * A method for injecting an arbitrary input event.
+ * <p>
+ * <strong>Note:</strong> It is caller's responsibility to recycle the event.
+ * </p>
+ * @param event The event to inject.
+ * @param sync Whether to inject the event synchronously.
+ * @return Whether event injection succeeded.
+ */
+ public boolean injectInputEvent(InputEvent event, boolean sync) {
+ synchronized (mLock) {
+ throwIfNotConnectedLocked();
+ }
+ try {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Injecting: " + event + " sync: " + sync);
+ }
+ // Calling out without a lock held.
+ return mUiAutomationConnection.injectInputEvent(event, sync);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while injecting input event!", re);
+ }
+ return false;
+ }
+
+ /**
+ * Sets the device rotation. A client can freeze the rotation in
+ * desired state or freeze the rotation to its current state or
+ * unfreeze the rotation (rotating the device changes its rotation
+ * state).
+ *
+ * @param rotation The desired rotation.
+ * @return Whether the rotation was set successfully.
+ *
+ * @see #ROTATION_FREEZE_0
+ * @see #ROTATION_FREEZE_90
+ * @see #ROTATION_FREEZE_180
+ * @see #ROTATION_FREEZE_270
+ * @see #ROTATION_FREEZE_CURRENT
+ * @see #ROTATION_UNFREEZE
+ */
+ public boolean setRotation(int rotation) {
+ synchronized (mLock) {
+ throwIfNotConnectedLocked();
+ }
+ switch (rotation) {
+ case ROTATION_FREEZE_0:
+ case ROTATION_FREEZE_90:
+ case ROTATION_FREEZE_180:
+ case ROTATION_FREEZE_270:
+ case ROTATION_UNFREEZE:
+ case ROTATION_FREEZE_CURRENT: {
+ try {
+ // Calling out without a lock held.
+ mUiAutomationConnection.setRotation(rotation);
+ return true;
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while setting rotation!", re);
+ }
+ } return false;
+ default: {
+ throw new IllegalArgumentException("Invalid rotation.");
+ }
+ }
+ }
+
+ /**
+ * Executes a command and waits for a specific accessibility event up to a
+ * given wait timeout. To detect a sequence of events one can implement a
+ * filter that keeps track of seen events of the expected sequence and
+ * returns true after the last event of that sequence is received.
+ * <p>
+ * <strong>Note:</strong> It is caller's responsibility to recycle the returned event.
+ * </p>
+ * @param command The command to execute.
+ * @param filter Filter that recognizes the expected event.
+ * @param timeoutMillis The wait timeout in milliseconds.
+ *
+ * @throws TimeoutException If the expected event is not received within the timeout.
+ */
+ public AccessibilityEvent executeAndWaitForEvent(Runnable command,
+ Predicate<AccessibilityEvent> filter, long timeoutMillis) throws TimeoutException {
+ synchronized (mLock) {
+ throwIfNotConnectedLocked();
+
+ mEventQueue.clear();
+ // Prepare to wait for an event.
+ mWaitingForEventDelivery = true;
+
+ // We will ignore events from previous interactions.
+ final long executionStartTimeMillis = SystemClock.uptimeMillis();
+
+ // Execute the command.
+ command.run();
+ try {
+ // Wait for the event.
+ final long startTimeMillis = SystemClock.uptimeMillis();
+ while (true) {
+ // Drain the event queue
+ while (!mEventQueue.isEmpty()) {
+ AccessibilityEvent event = mEventQueue.remove(0);
+ // Ignore events from previous interactions.
+ if (event.getEventTime() <= executionStartTimeMillis) {
+ continue;
+ }
+ if (filter.apply(event)) {
+ return event;
+ }
+ event.recycle();
+ }
+ // Check if timed out and if not wait.
+ final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+ final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis;
+ if (remainingTimeMillis <= 0) {
+ throw new TimeoutException("Expected event not received within: "
+ + timeoutMillis + " ms.");
+ }
+ try {
+ mLock.wait(remainingTimeMillis);
+ } catch (InterruptedException ie) {
+ /* ignore */
+ }
+ }
+ } finally {
+ mWaitingForEventDelivery = false;
+ mEventQueue.clear();
+ mLock.notifyAll();
+ }
+ }
+ }
+
+ /**
+ * Waits for the accessibility event stream to become idle, which is not to
+ * have received an accessibility event within <code>idleTimeoutMillis</code>.
+ * The total time spent to wait for an idle accessibility event stream is bounded
+ * by the <code>globalTimeoutMillis</code>.
+ *
+ * @param idleTimeoutMillis The timeout in milliseconds between two events
+ * to consider the device idle.
+ * @param globalTimeoutMillis The maximal global timeout in milliseconds in
+ * which to wait for an idle state.
+ *
+ * @throws TimeoutException If no idle state was detected within
+ * <code>globalTimeoutMillis.</code>
+ */
+ public void waitForIdle(long idleTimeoutMillis, long globalTimeoutMillis)
+ throws TimeoutException {
+ synchronized (mLock) {
+ throwIfNotConnectedLocked();
+
+ final long startTimeMillis = SystemClock.uptimeMillis();
+ if (mLastEventTimeMillis <= 0) {
+ mLastEventTimeMillis = startTimeMillis;
+ }
+
+ while (true) {
+ final long currentTimeMillis = SystemClock.uptimeMillis();
+ // Did we get idle state within the global timeout?
+ final long elapsedGlobalTimeMillis = currentTimeMillis - startTimeMillis;
+ final long remainingGlobalTimeMillis =
+ globalTimeoutMillis - elapsedGlobalTimeMillis;
+ if (remainingGlobalTimeMillis <= 0) {
+ throw new TimeoutException("No idle state with idle timeout: "
+ + idleTimeoutMillis + " within global timeout: "
+ + globalTimeoutMillis);
+ }
+ // Did we get an idle state within the idle timeout?
+ final long elapsedIdleTimeMillis = currentTimeMillis - mLastEventTimeMillis;
+ final long remainingIdleTimeMillis = idleTimeoutMillis - elapsedIdleTimeMillis;
+ if (remainingIdleTimeMillis <= 0) {
+ return;
+ }
+ try {
+ mLock.wait(remainingIdleTimeMillis);
+ } catch (InterruptedException ie) {
+ /* ignore */
+ }
+ }
+ }
+ }
+
+ /**
+ * Takes a screenshot.
+ *
+ * @return The screenshot bitmap on success, null otherwise.
+ */
+ public Bitmap takeScreenshot() {
+ synchronized (mLock) {
+ throwIfNotConnectedLocked();
+ }
+ Display display = DisplayManagerGlobal.getInstance()
+ .getRealDisplay(Display.DEFAULT_DISPLAY);
+ Point displaySize = new Point();
+ display.getRealSize(displaySize);
+ final int displayWidth = displaySize.x;
+ final int displayHeight = displaySize.y;
+
+ final float screenshotWidth;
+ final float screenshotHeight;
+
+ final int rotation = display.getRotation();
+ switch (rotation) {
+ case ROTATION_FREEZE_0: {
+ screenshotWidth = displayWidth;
+ screenshotHeight = displayHeight;
+ } break;
+ case ROTATION_FREEZE_90: {
+ screenshotWidth = displayHeight;
+ screenshotHeight = displayWidth;
+ } break;
+ case ROTATION_FREEZE_180: {
+ screenshotWidth = displayWidth;
+ screenshotHeight = displayHeight;
+ } break;
+ case ROTATION_FREEZE_270: {
+ screenshotWidth = displayHeight;
+ screenshotHeight = displayWidth;
+ } break;
+ default: {
+ throw new IllegalArgumentException("Invalid rotation: "
+ + rotation);
+ }
+ }
+
+ // Take the screenshot
+ Bitmap screenShot = null;
+ try {
+ // Calling out without a lock held.
+ screenShot = mUiAutomationConnection.takeScreenshot((int) screenshotWidth,
+ (int) screenshotHeight);
+ if (screenShot == null) {
+ return null;
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while taking screnshot!", re);
+ return null;
+ }
+
+ // Rotate the screenshot to the current orientation
+ if (rotation != ROTATION_FREEZE_0) {
+ Bitmap unrotatedScreenShot = Bitmap.createBitmap(displayWidth, displayHeight,
+ Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(unrotatedScreenShot);
+ canvas.translate(unrotatedScreenShot.getWidth() / 2,
+ unrotatedScreenShot.getHeight() / 2);
+ canvas.rotate(getDegreesForRotation(rotation));
+ canvas.translate(- screenshotWidth / 2, - screenshotHeight / 2);
+ canvas.drawBitmap(screenShot, 0, 0, null);
+ canvas.setBitmap(null);
+ screenShot = unrotatedScreenShot;
+ }
+
+ // Optimization
+ screenShot.setHasAlpha(false);
+
+ return screenShot;
+ }
+
+ private static float getDegreesForRotation(int value) {
+ switch (value) {
+ case Surface.ROTATION_90: {
+ return 360f - 90f;
+ }
+ case Surface.ROTATION_180: {
+ return 360f - 180f;
+ }
+ case Surface.ROTATION_270: {
+ return 360f - 270f;
+ } default: {
+ return 0;
+ }
+ }
+ }
+
+ private boolean isConnectedLocked() {
+ return mConnectionId != CONNECTION_ID_UNDEFINED;
+ }
+
+ private void throwIfConnectedLocked() {
+ if (mConnectionId != CONNECTION_ID_UNDEFINED) {
+ throw new IllegalStateException("UiAutomation not connected!");
+ }
+ }
+
+ private void throwIfNotConnectedLocked() {
+ if (!isConnectedLocked()) {
+ throw new IllegalStateException("UiAutomation not connected!");
+ }
+ }
+
+ private class IAccessibilityServiceClientImpl extends IAccessibilityServiceClientWrapper {
+
+ public IAccessibilityServiceClientImpl(Looper looper) {
+ super(null, looper, new Callbacks() {
+ @Override
+ public void onSetConnectionId(int connectionId) {
+ synchronized (mLock) {
+ mConnectionId = connectionId;
+ mLock.notifyAll();
+ }
+ }
+
+ @Override
+ public void onServiceConnected() {
+ /* do nothing */
+ }
+
+ @Override
+ public void onInterrupt() {
+ /* do nothing */
+ }
+
+ @Override
+ public boolean onGesture(int gestureId) {
+ /* do nothing */
+ return false;
+ }
+
+ @Override
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ synchronized (mLock) {
+ mLastEventTimeMillis = event.getEventTime();
+ if (mWaitingForEventDelivery) {
+ mEventQueue.add(AccessibilityEvent.obtain(event));
+ }
+ mLock.notifyAll();
+ }
+ // Calling out only without a lock held.
+ final OnAccessibilityEventListener listener = mOnAccessibilityEventListener;
+ if (listener != null) {
+ listener.onAccessibilityEvent(AccessibilityEvent.obtain(event));
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
new file mode 100644
index 0000000..9b5857f
--- /dev/null
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.IAccessibilityServiceClient;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.hardware.input.InputManager;
+import android.os.Binder;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.view.IWindowManager;
+import android.view.InputEvent;
+import android.view.Surface;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.IAccessibilityManager;
+
+/**
+ * This is a remote object that is passed from the shell to an instrumentation
+ * for enabling access to privileged operations which the shell can do and the
+ * instrumentation cannot. These privileged operations are needed for implementing
+ * a {@link UiAutomation} that enables across application testing by simulating
+ * user actions and performing screen introspection.
+ *
+ * @hide
+ */
+public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
+
+ private static final int INITIAL_FROZEN_ROTATION_UNSPECIFIED = -1;
+
+ private final IWindowManager mWindowManager = IWindowManager.Stub.asInterface(
+ ServiceManager.getService(Service.WINDOW_SERVICE));
+
+ private final Object mLock = new Object();
+
+ private int mInitialFrozenRotation = INITIAL_FROZEN_ROTATION_UNSPECIFIED;
+
+ private IAccessibilityServiceClient mClient;
+
+ private boolean mIsShutdown;
+
+ private int mOwningUid;
+
+ public void connect(IAccessibilityServiceClient client) {
+ if (client == null) {
+ throw new IllegalArgumentException("Client cannot be null!");
+ }
+ synchronized (mLock) {
+ throwIfShutdownLocked();
+ if (isConnectedLocked()) {
+ throw new IllegalStateException("Already connected.");
+ }
+ mOwningUid = Binder.getCallingUid();
+ registerUiTestAutomationServiceLocked(client);
+ storeRotationStateLocked();
+ }
+ }
+
+ @Override
+ public void disconnect() {
+ synchronized (mLock) {
+ throwIfCalledByNotTrustedUidLocked();
+ throwIfShutdownLocked();
+ if (!isConnectedLocked()) {
+ throw new IllegalStateException("Already disconnected.");
+ }
+ mOwningUid = -1;
+ unregisterUiTestAutomationServiceLocked();
+ restoreRotationStateLocked();
+ }
+ }
+
+ @Override
+ public boolean injectInputEvent(InputEvent event, boolean sync) {
+ synchronized (mLock) {
+ throwIfCalledByNotTrustedUidLocked();
+ throwIfShutdownLocked();
+ throwIfNotConnectedLocked();
+ }
+ final int mode = (sync) ? InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH
+ : InputManager.INJECT_INPUT_EVENT_MODE_ASYNC;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return InputManager.getInstance().injectInputEvent(event, mode);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public boolean setRotation(int rotation) {
+ synchronized (mLock) {
+ throwIfCalledByNotTrustedUidLocked();
+ throwIfShutdownLocked();
+ throwIfNotConnectedLocked();
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (rotation == UiAutomation.ROTATION_UNFREEZE) {
+ mWindowManager.thawRotation();
+ } else {
+ mWindowManager.freezeRotation(rotation);
+ }
+ return true;
+ } catch (RemoteException re) {
+ /* ignore */
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return false;
+ }
+
+ @Override
+ public Bitmap takeScreenshot(int width, int height) {
+ synchronized (mLock) {
+ throwIfCalledByNotTrustedUidLocked();
+ throwIfShutdownLocked();
+ throwIfNotConnectedLocked();
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return Surface.screenshot(width, height);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void shutdown() {
+ synchronized (mLock) {
+ throwIfCalledByNotTrustedUidLocked();
+ throwIfShutdownLocked();
+ mIsShutdown = true;
+ if (isConnectedLocked()) {
+ disconnect();
+ }
+ }
+ }
+
+ private void registerUiTestAutomationServiceLocked(IAccessibilityServiceClient client) {
+ IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface(
+ ServiceManager.getService(Context.ACCESSIBILITY_SERVICE));
+ AccessibilityServiceInfo info = new AccessibilityServiceInfo();
+ info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
+ info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
+ info.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
+ | AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS;
+ try {
+ // Calling out with a lock held is fine since if the system
+ // process is gone the client calling in will be killed.
+ manager.registerUiTestAutomationService(client, info);
+ mClient = client;
+ } catch (RemoteException re) {
+ throw new IllegalStateException("Error while registering UiTestAutomationService.", re);
+ }
+ }
+
+ private void unregisterUiTestAutomationServiceLocked() {
+ IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface(
+ ServiceManager.getService(Context.ACCESSIBILITY_SERVICE));
+ try {
+ // Calling out with a lock held is fine since if the system
+ // process is gone the client calling in will be killed.
+ manager.unregisterUiTestAutomationService(mClient);
+ mClient = null;
+ } catch (RemoteException re) {
+ throw new IllegalStateException("Error while unregistering UiTestAutomationService",
+ re);
+ }
+ }
+
+ private void storeRotationStateLocked() {
+ try {
+ if (mWindowManager.isRotationFrozen()) {
+ // Calling out with a lock held is fine since if the system
+ // process is gone the client calling in will be killed.
+ mInitialFrozenRotation = mWindowManager.getRotation();
+ }
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+
+ private void restoreRotationStateLocked() {
+ try {
+ if (mInitialFrozenRotation != INITIAL_FROZEN_ROTATION_UNSPECIFIED) {
+ // Calling out with a lock held is fine since if the system
+ // process is gone the client calling in will be killed.
+ mWindowManager.freezeRotation(mInitialFrozenRotation);
+ } else {
+ // Calling out with a lock held is fine since if the system
+ // process is gone the client calling in will be killed.
+ mWindowManager.thawRotation();
+ }
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+
+ private boolean isConnectedLocked() {
+ return mClient != null;
+ }
+
+ private void throwIfShutdownLocked() {
+ if (mIsShutdown) {
+ throw new IllegalStateException("Connection shutdown!");
+ }
+ }
+
+ private void throwIfNotConnectedLocked() {
+ if (!isConnectedLocked()) {
+ throw new IllegalStateException("Not connected!");
+ }
+ }
+
+ private void throwIfCalledByNotTrustedUidLocked() {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != mOwningUid && mOwningUid != Process.SYSTEM_UID
+ && callingUid != 0 /*root*/) {
+ throw new SecurityException("Calling from not trusted UID!");
+ }
+ }
+}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 4b977ab..e66efd5 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -191,11 +191,8 @@
String selection, String[] selectionArgs, String sortOrder,
ICancellationSignal cancellationSignal) {
if (enforceReadPermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
- // The read is not allowed... to fake it out, we replace the given
- // selection statement with a dummy one that will always be false.
- // This way we will get a cursor back that has the correct structure
- // but contains no rows.
- selection = "'A' = 'B'";
+ return rejectQuery(uri, projection, selection, selectionArgs, sortOrder,
+ CancellationSignal.fromTransport(cancellationSignal));
}
return ContentProvider.this.query(uri, projection, selection, selectionArgs, sortOrder,
CancellationSignal.fromTransport(cancellationSignal));
@@ -209,12 +206,7 @@
@Override
public Uri insert(String callingPkg, Uri uri, ContentValues initialValues) {
if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
- // If not allowed, we need to return some reasonable URI. Maybe the
- // content provider should be responsible for this, but for now we
- // will just return the base URI with a dummy '0' tagged on to it.
- // You shouldn't be able to read if you can't write, anyway, so it
- // shouldn't matter much what is returned.
- return uri.buildUpon().appendPath("0").build();
+ return rejectInsert(uri, initialValues);
}
return ContentProvider.this.insert(uri, initialValues);
}
@@ -592,6 +584,31 @@
}
/**
+ * @hide
+ * Implementation when a caller has performed a query on the content
+ * provider, but that call has been rejected for the operation given
+ * to {@link #setAppOps(int, int)}. The default implementation
+ * rewrites the <var>selection</var> argument to include a condition
+ * that is never true (so will always result in an empty cursor)
+ * and calls through to {@link #query(android.net.Uri, String[], String, String[],
+ * String, android.os.CancellationSignal)} with that.
+ */
+ public Cursor rejectQuery(Uri uri, String[] projection,
+ String selection, String[] selectionArgs, String sortOrder,
+ CancellationSignal cancellationSignal) {
+ // The read is not allowed... to fake it out, we replace the given
+ // selection statement with a dummy one that will always be false.
+ // This way we will get a cursor back that has the correct structure
+ // but contains no rows.
+ if (selection == null) {
+ selection = "'A' = 'B'";
+ } else {
+ selection = "'A' = 'B' AND (" + selection + ")";
+ }
+ return query(uri, projection, selection, selectionArgs, sortOrder, cancellationSignal);
+ }
+
+ /**
* Implement this to handle query requests from clients.
* This method can be called from multiple threads, as described in
* <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
@@ -739,6 +756,23 @@
public abstract String getType(Uri uri);
/**
+ * @hide
+ * Implementation when a caller has performed an insert on the content
+ * provider, but that call has been rejected for the operation given
+ * to {@link #setAppOps(int, int)}. The default implementation simply
+ * returns a dummy URI that is the base URI with a 0 path element
+ * appended.
+ */
+ public Uri rejectInsert(Uri uri, ContentValues values) {
+ // If not allowed, we need to return some reasonable URI. Maybe the
+ // content provider should be responsible for this, but for now we
+ // will just return the base URI with a dummy '0' tagged on to it.
+ // You shouldn't be able to read if you can't write, anyway, so it
+ // shouldn't matter much what is returned.
+ return uri.buildUpon().appendPath("0").build();
+ }
+
+ /**
* Implement this to handle requests to insert a new row.
* As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()}
* after inserting.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index dc367dd..434946c 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -883,7 +883,7 @@
* Activity Action: Allow the user to select a particular kind of data and
* return it. This is different than {@link #ACTION_PICK} in that here we
* just say what kind of data is desired, not a URI of existing data from
- * which the user can pick. A ACTION_GET_CONTENT could allow the user to
+ * which the user can pick. An ACTION_GET_CONTENT could allow the user to
* create the data as it runs (for example taking a picture or recording a
* sound), let them browse over the web and download the desired data,
* etc.
@@ -917,12 +917,17 @@
* from a remote server but not already on the local device (thus requiring
* they be downloaded when opened).
* <p>
+ * If the caller can handle multiple returned items (the user performing
+ * multiple selection), then it can specify {@link #EXTRA_ALLOW_MULTIPLE}
+ * to indicate this.
+ * <p>
* Input: {@link #getType} is the desired MIME type to retrieve. Note
* that no URI is supplied in the intent, as there are no constraints on
* where the returned data originally comes from. You may also include the
* {@link #CATEGORY_OPENABLE} if you can only accept data that can be
* opened as a stream. You may use {@link #EXTRA_LOCAL_ONLY} to limit content
- * selection to local data.
+ * selection to local data. You may use {@link #EXTRA_ALLOW_MULTIPLE} to
+ * allow the user to select multiple items.
* <p>
* Output: The URI of the item that was picked. This must be a content:
* URI so that any receiver can access it.
@@ -3045,6 +3050,17 @@
"android.intent.extra.LOCAL_ONLY";
/**
+ * Used to indicate that a {@link #ACTION_GET_CONTENT} intent can allow the
+ * user to select and return multiple items. This is a boolean extra; the default
+ * is false. If true, an implementation of ACTION_GET_CONTENT is allowed to
+ * present the user with a UI where they can pick multiple items that are all
+ * returned to the caller. When this happens, they should be returned as
+ * the {@link #getClipData()} part of the result Intent.
+ */
+ public static final String EXTRA_ALLOW_MULTIPLE =
+ "android.intent.extra.ALLOW_MULTIPLE";
+
+ /**
* The userHandle carried with broadcast intents related to addition, removal and switching of users
* - {@link #ACTION_USER_ADDED}, {@link #ACTION_USER_REMOVED} and {@link #ACTION_USER_SWITCHED}.
* @hide
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 0445b39..a368451 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -210,6 +210,8 @@
List<PackageInfo> getPreferredPackages(int flags);
+ void resetPreferredActivities(int userId);
+
void addPreferredActivity(in IntentFilter filter, int match,
in ComponentName[] set, in ComponentName activity, int userId);
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index 5ad60ec..222578a 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -48,10 +48,10 @@
// Barriers are indicated by messages with a null target whose arg1 field carries the token.
private int mNextBarrierToken;
- private native void nativeInit();
- private native void nativeDestroy();
- private native void nativePollOnce(int ptr, int timeoutMillis);
- private native void nativeWake(int ptr);
+ private native static int nativeInit();
+ private native static void nativeDestroy(int ptr);
+ private native static void nativePollOnce(int ptr, int timeoutMillis);
+ private native static void nativeWake(int ptr);
/**
* Callback interface for discovering when a thread is going to block
@@ -102,18 +102,25 @@
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
- nativeInit();
+ mPtr = nativeInit();
}
@Override
protected void finalize() throws Throwable {
try {
- nativeDestroy();
+ dispose();
} finally {
super.finalize();
}
}
+ private void dispose() {
+ if (mPtr != 0) {
+ nativeDestroy(mPtr);
+ mPtr = 0;
+ }
+ }
+
final Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
@@ -126,6 +133,7 @@
synchronized (this) {
if (mQuiting) {
+ dispose();
return null;
}
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index f52b5e3..c1af7a5 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -140,7 +140,10 @@
* Listener that will be called when the TTS service has
* completed synthesizing an utterance. This is only called if the utterance
* has an utterance ID (see {@link TextToSpeech.Engine#KEY_PARAM_UTTERANCE_ID}).
+ *
+ * @deprecated Use {@link UtteranceProgressListener} instead.
*/
+ @Deprecated
public interface OnUtteranceCompletedListener {
/**
* Called when an utterance has been synthesized.
@@ -236,19 +239,28 @@
/**
* Indicates erroneous data when checking the installation status of the resources used by
* the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
+ *
+ * @deprecated Use CHECK_VOICE_DATA_FAIL instead.
*/
+ @Deprecated
public static final int CHECK_VOICE_DATA_BAD_DATA = -1;
/**
* Indicates missing resources when checking the installation status of the resources used
* by the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
+ *
+ * @deprecated Use CHECK_VOICE_DATA_FAIL instead.
*/
+ @Deprecated
public static final int CHECK_VOICE_DATA_MISSING_DATA = -2;
/**
* Indicates missing storage volume when checking the installation status of the resources
* used by the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
+ *
+ * @deprecated Use CHECK_VOICE_DATA_FAIL instead.
*/
+ @Deprecated
public static final int CHECK_VOICE_DATA_MISSING_VOLUME = -3;
/**
@@ -284,9 +296,8 @@
"android.speech.tts.engine.INSTALL_TTS_DATA";
/**
- * Broadcast Action: broadcast to signal the completion of the installation of
- * the data files used by the synthesis engine. Success or failure is indicated in the
- * {@link #EXTRA_TTS_DATA_INSTALLED} extra.
+ * Broadcast Action: broadcast to signal the change in the list of available
+ * languages or/and their features.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_TTS_DATA_INSTALLED =
@@ -299,20 +310,16 @@
* return one of the following codes:
* {@link #CHECK_VOICE_DATA_PASS},
* {@link #CHECK_VOICE_DATA_FAIL},
- * {@link #CHECK_VOICE_DATA_BAD_DATA},
- * {@link #CHECK_VOICE_DATA_MISSING_DATA}, or
- * {@link #CHECK_VOICE_DATA_MISSING_VOLUME}.
* <p> Moreover, the data received in the activity result will contain the following
* fields:
* <ul>
- * <li>{@link #EXTRA_VOICE_DATA_ROOT_DIRECTORY} which
- * indicates the path to the location of the resource files,</li>
- * <li>{@link #EXTRA_VOICE_DATA_FILES} which contains
- * the list of all the resource files,</li>
- * <li>and {@link #EXTRA_VOICE_DATA_FILES_INFO} which
- * contains, for each resource file, the description of the language covered by
- * the file in the xxx-YYY format, where xxx is the 3-letter ISO language code,
- * and YYY is the 3-letter ISO country code.</li>
+ * <li>{@link #EXTRA_AVAILABLE_VOICES} which contains an ArrayList<String> of all the
+ * available voices. The format of each voice is: lang-COUNTRY-variant where COUNTRY and
+ * variant are optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE").</li>
+ * <li>{@link #EXTRA_UNAVAILABLE_VOICES} which contains an ArrayList<String> of all the
+ * unavailable voices (ones that user can install). The format of each voice is:
+ * lang-COUNTRY-variant where COUNTRY and variant are optional (ie, "eng" or
+ * "eng-USA" or "eng-USA-FEMALE").</li>
* </ul>
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
@@ -320,37 +327,33 @@
"android.speech.tts.engine.CHECK_TTS_DATA";
/**
- * Activity intent for getting some sample text to use for demonstrating TTS.
+ * Activity intent for getting some sample text to use for demonstrating TTS. Specific
+ * locale have to be requested by passing following extra parameters:
+ * <ul>
+ * <li>language</li>
+ * <li>country</li>
+ * <li>variant</li>
+ * </ul>
*
- * @hide This intent was used by engines written against the old API.
- * Not sure if it should be exposed.
+ * Upon completion, the activity result may contain the following fields:
+ * <ul>
+ * <li>{@link #EXTRA_SAMPLE_TEXT} which contains an String with sample text.</li>
+ * </ul>
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_GET_SAMPLE_TEXT =
"android.speech.tts.engine.GET_SAMPLE_TEXT";
+ /**
+ * Extra information received with the {@link #ACTION_GET_SAMPLE_TEXT} intent result where
+ * the TextToSpeech engine returns an String with sample text for requested voice
+ */
+ public static final String EXTRA_SAMPLE_TEXT = "sampleText";
+
+
// extras for a TTS engine's check data activity
/**
- * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
- * the TextToSpeech engine specifies the path to its resources.
- */
- public static final String EXTRA_VOICE_DATA_ROOT_DIRECTORY = "dataRoot";
-
- /**
- * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
- * the TextToSpeech engine specifies the file names of its resources under the
- * resource path.
- */
- public static final String EXTRA_VOICE_DATA_FILES = "dataFiles";
-
- /**
- * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
- * the TextToSpeech engine specifies the locale associated with each resource file.
- */
- public static final String EXTRA_VOICE_DATA_FILES_INFO = "dataFilesInfo";
-
- /**
- * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
+ * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent result where
* the TextToSpeech engine returns an ArrayList<String> of all the available voices.
* The format of each voice is: lang-COUNTRY-variant where COUNTRY and variant are
* optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE").
@@ -358,7 +361,7 @@
public static final String EXTRA_AVAILABLE_VOICES = "availableVoices";
/**
- * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
+ * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent result where
* the TextToSpeech engine returns an ArrayList<String> of all the unavailable voices.
* The format of each voice is: lang-COUNTRY-variant where COUNTRY and variant are
* optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE").
@@ -366,22 +369,63 @@
public static final String EXTRA_UNAVAILABLE_VOICES = "unavailableVoices";
/**
+ * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent result where
+ * the TextToSpeech engine specifies the path to its resources.
+ *
+ * It may be used by language packages to find out where to put their data.
+ *
+ * @deprecated TTS engine implementation detail, this information has no use for
+ * text-to-speech API client.
+ */
+ @Deprecated
+ public static final String EXTRA_VOICE_DATA_ROOT_DIRECTORY = "dataRoot";
+
+ /**
+ * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent result where
+ * the TextToSpeech engine specifies the file names of its resources under the
+ * resource path.
+ *
+ * @deprecated TTS engine implementation detail, this information has no use for
+ * text-to-speech API client.
+ */
+ @Deprecated
+ public static final String EXTRA_VOICE_DATA_FILES = "dataFiles";
+
+ /**
+ * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent result where
+ * the TextToSpeech engine specifies the locale associated with each resource file.
+ *
+ * @deprecated TTS engine implementation detail, this information has no use for
+ * text-to-speech API client.
+ */
+ @Deprecated
+ public static final String EXTRA_VOICE_DATA_FILES_INFO = "dataFilesInfo";
+
+ /**
* Extra information sent with the {@link #ACTION_CHECK_TTS_DATA} intent where the
* caller indicates to the TextToSpeech engine which specific sets of voice data to
* check for by sending an ArrayList<String> of the voices that are of interest.
* The format of each voice is: lang-COUNTRY-variant where COUNTRY and variant are
* optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE").
+ *
+ * @deprecated Redundant functionality, checking for existence of specific sets of voice
+ * data can be done on client side.
*/
+ @Deprecated
public static final String EXTRA_CHECK_VOICE_DATA_FOR = "checkVoiceDataFor";
// extras for a TTS engine's data installation
/**
- * Extra information received with the {@link #ACTION_TTS_DATA_INSTALLED} intent.
+ * Extra information received with the {@link #ACTION_TTS_DATA_INSTALLED} intent result.
* It indicates whether the data files for the synthesis engine were successfully
* installed. The installation was initiated with the {@link #ACTION_INSTALL_TTS_DATA}
* intent. The possible values for this extra are
* {@link TextToSpeech#SUCCESS} and {@link TextToSpeech#ERROR}.
+ *
+ * @deprecated No longer in use. If client ise interested in information about what
+ * changed, is should send ACTION_CHECK_TTS_DATA intent to discover available voices.
*/
+ @Deprecated
public static final String EXTRA_TTS_DATA_INSTALLED = "dataInstalled";
// keys for the parameters passed with speak commands. Hidden keys are used internally
@@ -474,6 +518,10 @@
* for a description of how feature keys work. If set and supported by the engine
* as per {@link TextToSpeech#getFeatures(Locale)}, the engine must synthesize
* text on-device (without making network requests).
+ *
+ * @see TextToSpeech#speak(String, int, java.util.HashMap)
+ * @see TextToSpeech#synthesizeToFile(String, java.util.HashMap, String)
+ * @see TextToSpeech#getFeatures(java.util.Locale)
*/
public static final String KEY_FEATURE_EMBEDDED_SYNTHESIS = "embeddedTts";
}
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index ba82d79..81c25d8 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -16,8 +16,8 @@
package android.view;
-import static android.view.accessibility.AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS;
-
+import android.app.ActivityThread;
+import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
@@ -34,6 +34,7 @@
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import com.android.internal.os.SomeArgs;
+import com.android.internal.util.Predicate;
import java.util.ArrayList;
import java.util.HashMap;
@@ -49,7 +50,7 @@
*/
final class AccessibilityInteractionController {
- private ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList =
+ private final ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList =
new ArrayList<AccessibilityNodeInfo>();
private final Handler mHandler;
@@ -69,6 +70,8 @@
private final Rect mTempRect1 = new Rect();
private final Rect mTempRect2 = new Rect();
+ private AddNodeInfosForViewId mAddNodeInfosForViewId;
+
public AccessibilityInteractionController(ViewRootImpl viewRootImpl) {
Looper looper = viewRootImpl.mHandler.getLooper();
mMyLooperThreadId = looper.getThread().getId();
@@ -135,8 +138,7 @@
if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
return;
}
- mViewRootImpl.mAttachInfo.mIncludeNotImportantViews =
- (flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
+ mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
View root = null;
if (accessibilityViewId == AccessibilityNodeInfo.UNDEFINED) {
root = mViewRootImpl.mView;
@@ -148,7 +150,7 @@
}
} finally {
try {
- mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false;
+ mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
if (spec != null) {
spec.recycle();
@@ -161,19 +163,19 @@
}
}
- public void findAccessibilityNodeInfoByViewIdClientThread(long accessibilityNodeId,
- int viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback,
+ public void findAccessibilityNodeInfosByViewIdClientThread(long accessibilityNodeId,
+ String viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback,
int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
Message message = mHandler.obtainMessage();
- message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID;
+ message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID;
message.arg1 = flags;
message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
SomeArgs args = SomeArgs.obtain();
- args.argi1 = viewId;
- args.argi2 = interactionId;
+ args.argi1 = interactionId;
args.arg1 = callback;
args.arg2 = spec;
+ args.arg3 = viewId;
message.obj = args;
@@ -189,26 +191,26 @@
}
}
- private void findAccessibilityNodeInfoByViewIdUiThread(Message message) {
+ private void findAccessibilityNodeInfosByViewIdUiThread(Message message) {
final int flags = message.arg1;
final int accessibilityViewId = message.arg2;
SomeArgs args = (SomeArgs) message.obj;
- final int viewId = args.argi1;
- final int interactionId = args.argi2;
+ final int interactionId = args.argi1;
final IAccessibilityInteractionConnectionCallback callback =
(IAccessibilityInteractionConnectionCallback) args.arg1;
final MagnificationSpec spec = (MagnificationSpec) args.arg2;
+ final String viewId = (String) args.arg3;
args.recycle();
- AccessibilityNodeInfo info = null;
+ final List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
+ infos.clear();
try {
if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
return;
}
- mViewRootImpl.mAttachInfo.mIncludeNotImportantViews =
- (flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
+ mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
View root = null;
if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
root = findViewByAccessibilityId(accessibilityViewId);
@@ -216,19 +218,31 @@
root = mViewRootImpl.mView;
}
if (root != null) {
- View target = root.findViewById(viewId);
- if (target != null && isShown(target)) {
- info = target.createAccessibilityNodeInfo();
+ int resolvedViewId = root.getContext().getResources().getIdentifier(
+ viewId, "id", root.getContext().getPackageName());
+ if (resolvedViewId <= 0) {
+ resolvedViewId = ((Context) ActivityThread.currentActivityThread()
+ .getSystemContext()).getResources()
+ .getIdentifier(viewId, "id", "android");
}
+ if (resolvedViewId <= 0) {
+ return;
+ }
+ if (mAddNodeInfosForViewId == null) {
+ mAddNodeInfosForViewId = new AddNodeInfosForViewId();
+ }
+ mAddNodeInfosForViewId.init(resolvedViewId, infos);
+ root.findViewByPredicate(mAddNodeInfosForViewId);
+ mAddNodeInfosForViewId.reset();
}
} finally {
try {
- mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false;
- applyAppScaleAndMagnificationSpecIfNeeded(info, spec);
+ mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
if (spec != null) {
spec.recycle();
}
- callback.setFindAccessibilityNodeInfoResult(info, interactionId);
+ callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
} catch (RemoteException re) {
/* ignore - the other side will time out */
}
@@ -281,8 +295,7 @@
if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
return;
}
- mViewRootImpl.mAttachInfo.mIncludeNotImportantViews =
- (flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
+ mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
View root = null;
if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
root = findViewByAccessibilityId(accessibilityViewId);
@@ -325,7 +338,7 @@
}
} finally {
try {
- mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false;
+ mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
if (spec != null) {
spec.recycle();
@@ -384,8 +397,7 @@
if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
return;
}
- mViewRootImpl.mAttachInfo.mIncludeNotImportantViews =
- (flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
+ mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
View root = null;
if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
root = findViewByAccessibilityId(accessibilityViewId);
@@ -426,7 +438,7 @@
}
} finally {
try {
- mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false;
+ mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
applyAppScaleAndMagnificationSpecIfNeeded(focused, spec);
if (spec != null) {
spec.recycle();
@@ -484,8 +496,7 @@
if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
return;
}
- mViewRootImpl.mAttachInfo.mIncludeNotImportantViews =
- (flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
+ mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
View root = null;
if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
root = findViewByAccessibilityId(accessibilityViewId);
@@ -500,7 +511,7 @@
}
} finally {
try {
- mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false;
+ mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
applyAppScaleAndMagnificationSpecIfNeeded(next, spec);
if (spec != null) {
spec.recycle();
@@ -561,8 +572,7 @@
if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
return;
}
- mViewRootImpl.mAttachInfo.mIncludeNotImportantViews =
- (flags & INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
+ mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
View target = null;
if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
target = findViewByAccessibilityId(accessibilityViewId);
@@ -580,7 +590,7 @@
}
} finally {
try {
- mViewRootImpl.mAttachInfo.mIncludeNotImportantViews = false;
+ mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
callback.setPerformAccessibilityActionResult(succeeded, interactionId);
} catch (RemoteException re) {
/* ignore - the other side will time out */
@@ -690,20 +700,20 @@
private final ArrayList<View> mTempViewList = new ArrayList<View>();
- public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int prefetchFlags,
+ public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int fetchFlags,
List<AccessibilityNodeInfo> outInfos) {
AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
if (provider == null) {
AccessibilityNodeInfo root = view.createAccessibilityNodeInfo();
if (root != null) {
outInfos.add(root);
- if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
+ if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
prefetchPredecessorsOfRealNode(view, outInfos);
}
- if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
+ if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
prefetchSiblingsOfRealNode(view, outInfos);
}
- if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
+ if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
prefetchDescendantsOfRealNode(view, outInfos);
}
}
@@ -711,13 +721,13 @@
AccessibilityNodeInfo root = provider.createAccessibilityNodeInfo(virtualViewId);
if (root != null) {
outInfos.add(root);
- if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
+ if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) != 0) {
prefetchPredecessorsOfVirtualNode(root, view, provider, outInfos);
}
- if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
+ if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0) {
prefetchSiblingsOfVirtualNode(root, view, provider, outInfos);
}
- if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
+ if ((fetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS) != 0) {
prefetchDescendantsOfVirtualNode(root, provider, outInfos);
}
}
@@ -920,7 +930,7 @@
private class PrivateHandler extends Handler {
private final static int MSG_PERFORM_ACCESSIBILITY_ACTION = 1;
private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 2;
- private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 3;
+ private final static int MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID = 3;
private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT = 4;
private final static int MSG_FIND_FOCUS = 5;
private final static int MSG_FOCUS_SEARCH = 6;
@@ -937,8 +947,8 @@
return "MSG_PERFORM_ACCESSIBILITY_ACTION";
case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID:
return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID";
- case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID:
- return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID";
+ case MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID:
+ return "MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID";
case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT:
return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT";
case MSG_FIND_FOCUS:
@@ -960,8 +970,8 @@
case MSG_PERFORM_ACCESSIBILITY_ACTION: {
perfromAccessibilityActionUiThread(message);
} break;
- case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID: {
- findAccessibilityNodeInfoByViewIdUiThread(message);
+ case MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID: {
+ findAccessibilityNodeInfosByViewIdUiThread(message);
} break;
case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT: {
findAccessibilityNodeInfosByTextUiThread(message);
@@ -977,4 +987,27 @@
}
}
}
+
+ private final class AddNodeInfosForViewId implements Predicate<View> {
+ private int mViewId = View.NO_ID;
+ private List<AccessibilityNodeInfo> mInfos;
+
+ public void init(int viewId, List<AccessibilityNodeInfo> infos) {
+ mViewId = viewId;
+ mInfos = infos;
+ }
+
+ public void reset() {
+ mViewId = View.NO_ID;
+ mInfos = null;
+ }
+
+ @Override
+ public boolean apply(View view) {
+ if (view.getId() == mViewId && isShown(view)) {
+ mInfos.add(view.createAccessibilityNodeInfo());
+ }
+ return false;
+ }
+ }
}
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 2b4260d..5e6125f 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -1868,7 +1868,7 @@
mDebugDataProvider.setupGraphPaint(mProfilePaint, i);
switch (graphType) {
case GraphDataProvider.GRAPH_TYPE_BARS:
- mGlCanvas.drawRects(mProfileShapes[i], count, mProfilePaint);
+ mGlCanvas.drawRects(mProfileShapes[i], count * 4, mProfilePaint);
break;
case GraphDataProvider.GRAPH_TYPE_LINES:
mGlCanvas.drawLines(mProfileShapes[i], 0, count * 4, mProfilePaint);
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 1ee2bb3..a9ad97f 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -190,6 +190,13 @@
void thawRotation();
/**
+ * Gets whether the rotation is frozen.
+ *
+ * @return Whether the rotation is frozen.
+ */
+ boolean isRotationFrozen();
+
+ /**
* Create a screenshot of the applications currently displayed.
*/
Bitmap screenshotApplications(IBinder appToken, int displayId, int maxWidth, int maxHeight);
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index c2a3e58..af818fa 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -302,27 +302,27 @@
public static final int KEYCODE_SWITCH_CHARSET = 95; // switch char-sets (Kanji,Katakana)
/** Key code constant: A Button key.
* On a game controller, the A button should be either the button labeled A
- * or the first button on the upper row of controller buttons. */
+ * or the first button on the bottom row of controller buttons. */
public static final int KEYCODE_BUTTON_A = 96;
/** Key code constant: B Button key.
* On a game controller, the B button should be either the button labeled B
- * or the second button on the upper row of controller buttons. */
+ * or the second button on the bottom row of controller buttons. */
public static final int KEYCODE_BUTTON_B = 97;
/** Key code constant: C Button key.
* On a game controller, the C button should be either the button labeled C
- * or the third button on the upper row of controller buttons. */
+ * or the third button on the bottom row of controller buttons. */
public static final int KEYCODE_BUTTON_C = 98;
/** Key code constant: X Button key.
* On a game controller, the X button should be either the button labeled X
- * or the first button on the lower row of controller buttons. */
+ * or the first button on the upper row of controller buttons. */
public static final int KEYCODE_BUTTON_X = 99;
/** Key code constant: Y Button key.
* On a game controller, the Y button should be either the button labeled Y
- * or the second button on the lower row of controller buttons. */
+ * or the second button on the upper row of controller buttons. */
public static final int KEYCODE_BUTTON_Y = 100;
/** Key code constant: Z Button key.
* On a game controller, the Z button should be either the button labeled Z
- * or the third button on the lower row of controller buttons. */
+ * or the third button on the upper row of controller buttons. */
public static final int KEYCODE_BUTTON_Z = 101;
/** Key code constant: L1 Button key.
* On a game controller, the L1 button should be either the button labeled L1 (or L)
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a4e4f37..b9babdc 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16,6 +16,7 @@
package android.view;
+import android.app.ActivityThread;
import android.content.ClipData;
import android.content.Context;
import android.content.res.Configuration;
@@ -5007,6 +5008,25 @@
if (label != null) {
info.setLabeledBy(label);
}
+
+ if ((mAttachInfo.mAccessibilityFetchFlags
+ & AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS) != 0) {
+ String viewId = null;
+ try {
+ viewId = getResources().getResourceName(mID);
+ } catch (Resources.NotFoundException nfe) {
+ /* ignore */
+ }
+ if (viewId == null) {
+ try {
+ viewId = ((Context) ActivityThread.currentActivityThread()
+ .getSystemContext()).getResources().getResourceName(mID);
+ } catch (Resources.NotFoundException nfe) {
+ /* ignore */
+ }
+ }
+ info.setViewId(viewId);
+ }
}
if (mLabelForId != View.NO_ID) {
@@ -6838,7 +6858,9 @@
*/
public boolean includeForAccessibility() {
if (mAttachInfo != null) {
- return mAttachInfo.mIncludeNotImportantViews || isImportantForAccessibility();
+ return (mAttachInfo.mAccessibilityFetchFlags
+ & AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0
+ || isImportantForAccessibility();
}
return false;
}
@@ -18004,10 +18026,12 @@
int mAccessibilityWindowId = View.NO_ID;
/**
- * Whether to ingore not exposed for accessibility Views when
- * reporting the view tree to accessibility services.
+ * Flags related to accessibility processing.
+ *
+ * @see AccessibilityNodeInfo#FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
+ * @see AccessibilityNodeInfo#FLAG_REPORT_VIEW_IDS
*/
- boolean mIncludeNotImportantViews;
+ int mAccessibilityFetchFlags;
/**
* The drawable for highlighting accessibility focus.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9f075c4..ba9eb89 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -5482,15 +5482,16 @@
}
@Override
- public void findAccessibilityNodeInfoByViewId(long accessibilityNodeId, int viewId,
- int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
+ public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId,
+ String viewId, int interactionId,
+ IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
ViewRootImpl viewRootImpl = mViewRootImpl.get();
if (viewRootImpl != null && viewRootImpl.mView != null) {
viewRootImpl.getAccessibilityInteractionController()
- .findAccessibilityNodeInfoByViewIdClientThread(accessibilityNodeId, viewId,
- interactionId, callback, flags, interrogatingPid, interrogatingTid,
- spec);
+ .findAccessibilityNodeInfosByViewIdClientThread(accessibilityNodeId,
+ viewId, interactionId, callback, flags, interrogatingPid,
+ interrogatingTid, spec);
} else {
// We cannot make the call and notify the caller so it does not wait.
try {
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 9377cfa..02be4db 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -1035,6 +1035,16 @@
public void keepScreenOnStoppedLw();
/**
+ * Gets the current user rotation mode.
+ *
+ * @return The rotation mode.
+ *
+ * @see WindowManagerPolicy#USER_ROTATION_LOCKED
+ * @see WindowManagerPolicy#USER_ROTATION_FREE
+ */
+ public int getUserRotationMode();
+
+ /**
* Inform the policy that the user has chosen a preferred orientation ("rotation lock").
*
* @param mode One of {@link WindowManagerPolicy#USER_ROTATION_LOCKED} or
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 67df684..84d7e72 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -230,23 +230,25 @@
* where to start the search. Use
* {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
* to start from the root.
- * @param viewId The id of the view.
- * @return An {@link AccessibilityNodeInfo} if found, null otherwise.
+ * @param viewId The fully qualified resource name of the view id to find.
+ * @return An list of {@link AccessibilityNodeInfo} if found, empty list otherwise.
*/
- public AccessibilityNodeInfo findAccessibilityNodeInfoByViewId(int connectionId,
- int accessibilityWindowId, long accessibilityNodeId, int viewId) {
+ public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(int connectionId,
+ int accessibilityWindowId, long accessibilityNodeId, String viewId) {
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
- final boolean success =connection.findAccessibilityNodeInfoByViewId(
+ final boolean success = connection.findAccessibilityNodeInfosByViewId(
accessibilityWindowId, accessibilityNodeId, viewId, interactionId, this,
Thread.currentThread().getId());
if (success) {
- AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
+ List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
- finalizeAndCacheAccessibilityNodeInfo(info, connectionId);
- return info;
+ if (infos != null) {
+ finalizeAndCacheAccessibilityNodeInfos(infos, connectionId);
+ return infos;
+ }
}
} else {
if (DEBUG) {
@@ -259,7 +261,7 @@
+ " findAccessibilityNodeInfoByViewIdInActiveWindow", re);
}
}
- return null;
+ return Collections.emptyList();
}
/**
@@ -291,8 +293,10 @@
if (success) {
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
- finalizeAndCacheAccessibilityNodeInfos(infos, connectionId);
- return infos;
+ if (infos != null) {
+ finalizeAndCacheAccessibilityNodeInfos(infos, connectionId);
+ return infos;
+ }
}
} else {
if (DEBUG) {
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 6c03280..6d0a237 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -16,6 +16,7 @@
package android.view.accessibility;
+import android.accessibilityservice.AccessibilityServiceInfo;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Parcel;
@@ -78,7 +79,10 @@
public static final int FLAG_PREFETCH_DESCENDANTS = 0x00000004;
/** @hide */
- public static final int INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000008;
+ public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000008;
+
+ /** @hide */
+ public static final int FLAG_REPORT_VIEW_IDS = 0x00000010;
// Actions.
@@ -375,6 +379,7 @@
private CharSequence mClassName;
private CharSequence mText;
private CharSequence mContentDescription;
+ private CharSequence mViewId;
private final SparseLongArray mChildNodeIds = new SparseLongArray();
private int mActions;
@@ -729,6 +734,37 @@
}
/**
+ * Finds {@link AccessibilityNodeInfo}s by the fully qualified view id's resource
+ * name where a fully qualified id is of the from "package:id/id_resource_name".
+ * For example, if the target application's package is "foo.bar" and the id
+ * resource name is "baz", the fully qualified resource id is "foo.bar:id/baz".
+ *
+ * <p>
+ * <strong>Note:</strong> It is a client responsibility to recycle the
+ * received info by calling {@link AccessibilityNodeInfo#recycle()}
+ * to avoid creating of multiple instances.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> The primary usage of this API is for UI test automation
+ * and in order to report the fully qualified view id if an {@link AccessibilityNodeInfo}
+ * the client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS}
+ * flag when configuring his {@link android.accessibilityservice.AccessibilityService}.
+ * </p>
+ *
+ * @param viewId The fully qualified resource name of the view id to find.
+ * @return A list of node info.
+ */
+ public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(String viewId) {
+ enforceSealed();
+ if (!canPerformRequestOverConnection(mSourceNodeId)) {
+ return Collections.emptyList();
+ }
+ AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+ return client.findAccessibilityNodeInfosByViewId(mConnectionId, mWindowId, mSourceNodeId,
+ viewId);
+ }
+
+ /**
* Gets the parent.
* <p>
* <strong>Note:</strong> It is a client responsibility to recycle the
@@ -1373,6 +1409,38 @@
}
/**
+ * Sets the fully qualified resource name of the source view's id.
+ *
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param viewId The id resource name.
+ */
+ public void setViewId(CharSequence viewId) {
+ enforceNotSealed();
+ mViewId = viewId;
+ }
+
+ /**
+ * Gets the fully qualified resource name of the source view's id.
+ *
+ * <p>
+ * <strong>Note:</strong> The primary usage of this API is for UI test automation
+ * and in order to report the source view id of an {@link AccessibilityNodeInfo} the
+ * client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS}
+ * flag when configuring his {@link android.accessibilityservice.AccessibilityService}.
+ * </p>
+
+ * @return The id resource name.
+ */
+ public CharSequence getViewId() {
+ return mViewId;
+ }
+
+ /**
* Gets the value of a boolean property.
*
* @param property The property.
@@ -1614,6 +1682,7 @@
parcel.writeCharSequence(mClassName);
parcel.writeCharSequence(mText);
parcel.writeCharSequence(mContentDescription);
+ parcel.writeCharSequence(mViewId);
// Since instances of this class are fetched via synchronous i.e. blocking
// calls in IPCs we always recycle as soon as the instance is marshaled.
@@ -1639,6 +1708,7 @@
mClassName = other.mClassName;
mText = other.mText;
mContentDescription = other.mContentDescription;
+ mViewId = other.mViewId;
mActions= other.mActions;
mBooleanProperties = other.mBooleanProperties;
mMovementGranularities = other.mMovementGranularities;
@@ -1689,6 +1759,7 @@
mClassName = parcel.readCharSequence();
mText = parcel.readCharSequence();
mContentDescription = parcel.readCharSequence();
+ mViewId = parcel.readCharSequence();
}
/**
@@ -1711,6 +1782,7 @@
mClassName = null;
mText = null;
mContentDescription = null;
+ mViewId = null;
mActions = 0;
}
@@ -1855,6 +1927,7 @@
builder.append("; className: ").append(mClassName);
builder.append("; text: ").append(mText);
builder.append("; contentDescription: ").append(mContentDescription);
+ builder.append("; viewId: ").append(mViewId);
builder.append("; checkable: ").append(isCheckable());
builder.append("; checked: ").append(isChecked());
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
index c313b07..8d15472 100644
--- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
@@ -33,9 +33,9 @@
IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
long interrogatingTid, in MagnificationSpec spec);
- void findAccessibilityNodeInfoByViewId(long accessibilityNodeId, int viewId, int interactionId,
- IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
- long interrogatingTid, in MagnificationSpec spec);
+ void findAccessibilityNodeInfosByViewId(long accessibilityNodeId, String viewId,
+ int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
+ int interrogatingPid, long interrogatingTid, in MagnificationSpec spec);
void findAccessibilityNodeInfosByText(long accessibilityNodeId, String text, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 08e30aa..54c2ba5 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -81,6 +81,11 @@
private boolean mIsAuxIme;
/**
+ * Cavert: mForceDefault must be false for production. This flag is only for test.
+ */
+ private final boolean mForceDefault;
+
+ /**
* Constructor.
*
* @param context The Context in which we are parsing the input method.
@@ -108,6 +113,7 @@
ServiceInfo si = service.serviceInfo;
mId = new ComponentName(si.packageName, si.name).flattenToShortString();
mIsAuxIme = true;
+ mForceDefault = false;
PackageManager pm = context.getPackageManager();
String settingsActivityComponent = null;
@@ -215,13 +221,39 @@
mIsAuxIme = source.readInt() == 1;
mService = ResolveInfo.CREATOR.createFromParcel(source);
source.readTypedList(mSubtypes, InputMethodSubtype.CREATOR);
+ mForceDefault = false;
}
/**
- * Temporary API for creating a built-in input method.
+ * Temporary API for creating a built-in input method for test.
*/
public InputMethodInfo(String packageName, String className,
CharSequence label, String settingsActivity) {
+ this(buildDummyResolveInfo(packageName, className, label), false, settingsActivity, null,
+ 0, false);
+ }
+
+ /**
+ * Temporary API for creating a built-in input method for test.
+ * @hide
+ */
+ public InputMethodInfo(ResolveInfo ri, boolean isAuxIme,
+ String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId,
+ boolean forceDefault) {
+ final ServiceInfo si = ri.serviceInfo;
+ mService = ri;
+ mId = new ComponentName(si.packageName, si.name).flattenToShortString();
+ mSettingsActivityName = settingsActivity;
+ mIsDefaultResId = isDefaultResId;
+ mIsAuxIme = isAuxIme;
+ if (subtypes != null) {
+ mSubtypes.addAll(subtypes);
+ }
+ mForceDefault = forceDefault;
+ }
+
+ private static ResolveInfo buildDummyResolveInfo(String packageName, String className,
+ CharSequence label) {
ResolveInfo ri = new ResolveInfo();
ServiceInfo si = new ServiceInfo();
ApplicationInfo ai = new ApplicationInfo();
@@ -234,11 +266,7 @@
si.exported = true;
si.nonLocalizedLabel = label;
ri.serviceInfo = si;
- mService = ri;
- mId = new ComponentName(si.packageName, si.name).flattenToShortString();
- mSettingsActivityName = settingsActivity;
- mIsDefaultResId = 0;
- mIsAuxIme = false;
+ return ri;
}
/**
@@ -340,6 +368,22 @@
return mIsDefaultResId;
}
+ /**
+ * Return whether or not this ime is a default ime or not.
+ * @hide
+ */
+ public boolean isDefault(Context context) {
+ if (mForceDefault) {
+ return true;
+ }
+ try {
+ final Resources res = context.createPackageContext(getPackageName(), 0).getResources();
+ return res.getBoolean(getIsDefaultResourceId());
+ } catch (NameNotFoundException e) {
+ return false;
+ }
+ }
+
public void dump(Printer pw, String prefix) {
pw.println(prefix + "mId=" + mId
+ " mSettingsActivityName=" + mSettingsActivityName);
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 57bf0d3..396fd68 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -1374,9 +1374,11 @@
if (isEnabled()) {
if (getFirstVisiblePosition() > 0) {
info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+ info.setScrollable(true);
}
if (getLastVisiblePosition() < getCount() - 1) {
info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+ info.setScrollable(true);
}
}
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index 3d7e1ff..655d148 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -59,11 +59,53 @@
& ApplicationInfo.FLAG_SYSTEM) != 0;
}
- public static boolean isSystemImeThatHasEnglishSubtype(InputMethodInfo imi) {
+ public static boolean isSystemImeThatHasEnglishKeyboardSubtype(InputMethodInfo imi) {
if (!isSystemIme(imi)) {
return false;
}
- return containsSubtypeOf(imi, ENGLISH_LOCALE.getLanguage());
+ return containsSubtypeOf(imi, ENGLISH_LOCALE.getLanguage(), SUBTYPE_MODE_KEYBOARD);
+ }
+
+ private static boolean isSystemAuxilialyImeThatHashAutomaticSubtype(InputMethodInfo imi) {
+ if (!isSystemIme(imi)) {
+ return false;
+ }
+ if (!imi.isAuxiliaryIme()) {
+ return false;
+ }
+ final int subtypeCount = imi.getSubtypeCount();
+ for (int i = 0; i < subtypeCount; ++i) {
+ final InputMethodSubtype s = imi.getSubtypeAt(i);
+ if (s.overridesImplicitlyEnabledSubtype()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static ArrayList<InputMethodInfo> getDefaultEnabledImes(
+ Context context, boolean isSystemReady, ArrayList<InputMethodInfo> imis) {
+ final ArrayList<InputMethodInfo> retval = new ArrayList<InputMethodInfo>();
+ boolean auxilialyImeAdded = false;
+ for (int i = 0; i < imis.size(); ++i) {
+ final InputMethodInfo imi = imis.get(i);
+ if (isDefaultEnabledIme(isSystemReady, imi, context)) {
+ retval.add(imi);
+ if (imi.isAuxiliaryIme()) {
+ auxilialyImeAdded = true;
+ }
+ }
+ }
+ if (auxilialyImeAdded) {
+ return retval;
+ }
+ for (int i = 0; i < imis.size(); ++i) {
+ final InputMethodInfo imi = imis.get(i);
+ if (isSystemAuxilialyImeThatHashAutomaticSubtype(imi)) {
+ retval.add(imi);
+ }
+ }
+ return retval;
}
// TODO: Rename isSystemDefaultImeThatHasCurrentLanguageSubtype
@@ -77,14 +119,11 @@
}
if (imi.getIsDefaultResourceId() != 0) {
try {
- Resources res = context.createPackageContext(
- imi.getPackageName(), 0).getResources();
- if (res.getBoolean(imi.getIsDefaultResourceId())
- && containsSubtypeOf(imi, context.getResources().getConfiguration().
- locale.getLanguage())) {
+ if (imi.isDefault(context) && containsSubtypeOf(
+ imi, context.getResources().getConfiguration().locale.getLanguage(),
+ null /* mode */)) {
return true;
}
- } catch (PackageManager.NameNotFoundException ex) {
} catch (Resources.NotFoundException ex) {
}
}
@@ -97,15 +136,19 @@
public static boolean isDefaultEnabledIme(
boolean isSystemReady, InputMethodInfo imi, Context context) {
return isValidSystemDefaultIme(isSystemReady, imi, context)
- || isSystemImeThatHasEnglishSubtype(imi);
+ || isSystemImeThatHasEnglishKeyboardSubtype(imi);
}
- private static boolean containsSubtypeOf(InputMethodInfo imi, String language) {
+ private static boolean containsSubtypeOf(InputMethodInfo imi, String language, String mode) {
final int N = imi.getSubtypeCount();
for (int i = 0; i < N; ++i) {
- if (imi.getSubtypeAt(i).getLocale().startsWith(language)) {
- return true;
+ if (!imi.getSubtypeAt(i).getLocale().startsWith(language)) {
+ continue;
}
+ if(!TextUtils.isEmpty(mode) && !imi.getSubtypeAt(i).getMode().equalsIgnoreCase(mode)) {
+ continue;
+ }
+ return true;
}
return false;
}
@@ -141,7 +184,7 @@
while (i > 0) {
i--;
final InputMethodInfo imi = enabledImes.get(i);
- if (InputMethodUtils.isSystemImeThatHasEnglishSubtype(imi)
+ if (InputMethodUtils.isSystemImeThatHasEnglishKeyboardSubtype(imi)
&& !imi.isAuxiliaryIme()) {
return imi;
}
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index d24513a..fd7e3b0 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -76,18 +76,6 @@
private final String peerSecurityContext;
/**
- * A long-lived reference to the original command socket used to launch
- * this peer. If "peer wait" mode is specified, the process that requested
- * the new VM instance intends to track the lifetime of the spawned instance
- * via the command socket. In this case, the command socket is closed
- * in the Zygote and placed here in the spawned instance so that it will
- * not be collected and finalized. This field remains null at all times
- * in the original Zygote process, and in all spawned processes where
- * "peer-wait" mode was not requested.
- */
- private static LocalSocket sPeerWaitSocket = null;
-
- /**
* Constructs instance from connected socket.
*
* @param socket non-null; connected socket
@@ -298,11 +286,6 @@
* <li> --rlimit=r,c,m<i>tuple of values for setrlimit() call.
* <code>r</code> is the resource, <code>c</code> and <code>m</code>
* are the settings for current and max value.</i>
- * <li> --peer-wait indicates that the command socket should
- * be inherited by (and set to close-on-exec in) the spawned process
- * and used to track the lifetime of that process. The spawning process
- * then exits. Without this flag, it is retained by the spawning process
- * (and closed in the child) in expectation of a new spawn request.
* <li> --classpath=<i>colon-separated classpath</i> indicates
* that the specified class (which must b first non-flag argument) should
* be loaded from jar files in the specified classpath. Incompatible with
@@ -330,9 +313,6 @@
/** from --setgroups */
int[] gids;
- /** from --peer-wait */
- boolean peerWait;
-
/**
* From --enable-debugger, --enable-checkjni, --enable-assert,
* --enable-safemode, and --enable-jni-logging.
@@ -437,8 +417,6 @@
debugFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
} else if (arg.equals("--enable-assert")) {
debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
- } else if (arg.equals("--peer-wait")) {
- peerWait = true;
} else if (arg.equals("--runtime-init")) {
runtimeInit = true;
} else if (arg.startsWith("--seinfo=")) {
@@ -897,23 +875,8 @@
FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
throws ZygoteInit.MethodAndArgsCaller {
- /*
- * Close the socket, unless we're in "peer wait" mode, in which
- * case it's used to track the liveness of this process.
- */
-
- if (parsedArgs.peerWait) {
- try {
- ZygoteInit.setCloseOnExec(mSocket.getFileDescriptor(), true);
- sPeerWaitSocket = mSocket;
- } catch (IOException ex) {
- Log.e(TAG, "Zygote Child: error setting peer wait "
- + "socket to be close-on-exec", ex);
- }
- } else {
- closeSocket();
- ZygoteInit.closeServerSocket();
- }
+ closeSocket();
+ ZygoteInit.closeServerSocket();
if (descriptors != null) {
try {
@@ -1044,18 +1007,6 @@
return true;
}
- /*
- * If the peer wants to use the socket to wait on the
- * newly spawned process, then we're all done.
- */
- if (parsedArgs.peerWait) {
- try {
- mSocket.close();
- } catch (IOException ex) {
- Log.e(TAG, "Zygote: error closing sockets", ex);
- }
- return true;
- }
return false;
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 9e43749..7eddc9c 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -19,10 +19,8 @@
import static libcore.io.OsConstants.S_IRWXG;
import static libcore.io.OsConstants.S_IRWXO;
-import android.content.pm.ActivityInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
import android.net.LocalServerSocket;
import android.os.Debug;
import android.os.Process;
@@ -88,12 +86,6 @@
static final int GC_LOOP_COUNT = 10;
/**
- * If true, zygote forks for each peer. If false, a select loop is used
- * inside a single process. The latter is preferred.
- */
- private static final boolean ZYGOTE_FORK_MODE = false;
-
- /**
* The name of a resource file that contains classes to preload.
*/
private static final String PRELOADED_CLASSES = "preloaded-classes";
@@ -549,11 +541,7 @@
Log.i(TAG, "Accepting command socket connections");
- if (ZYGOTE_FORK_MODE) {
- runForkMode();
- } else {
- runSelectLoopMode();
- }
+ runSelectLoop();
closeServerSocket();
} catch (MethodAndArgsCaller caller) {
@@ -566,44 +554,6 @@
}
/**
- * Runs the zygote in accept-and-fork mode. In this mode, each peer
- * gets its own zygote spawner process. This code is retained for
- * reference only.
- *
- * @throws MethodAndArgsCaller in a child process when a main() should
- * be executed.
- */
- private static void runForkMode() throws MethodAndArgsCaller {
- while (true) {
- ZygoteConnection peer = acceptCommandPeer();
-
- int pid;
-
- pid = Zygote.fork();
-
- if (pid == 0) {
- // The child process should handle the peer requests
-
- // The child does not accept any more connections
- try {
- sServerSocket.close();
- } catch (IOException ex) {
- Log.e(TAG, "Zygote Child: error closing sockets", ex);
- } finally {
- sServerSocket = null;
- }
-
- peer.run();
- break;
- } else if (pid > 0) {
- peer.closeSocket();
- } else {
- throw new RuntimeException("Error invoking fork()");
- }
- }
- }
-
- /**
* Runs the zygote process's select loop. Accepts new connections as
* they happen, and reads commands from connections one spawn-request's
* worth at a time.
@@ -611,9 +561,9 @@
* @throws MethodAndArgsCaller in a child process when a main() should
* be executed.
*/
- private static void runSelectLoopMode() throws MethodAndArgsCaller {
- ArrayList<FileDescriptor> fds = new ArrayList();
- ArrayList<ZygoteConnection> peers = new ArrayList();
+ private static void runSelectLoop() throws MethodAndArgsCaller {
+ ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
+ ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
FileDescriptor[] fdArray = new FileDescriptor[4];
fds.add(sServerSocket.getFileDescriptor());
@@ -734,17 +684,6 @@
throws IOException;
/**
- * Sets the permitted and effective capability sets of this process.
- *
- * @param permittedCapabilities permitted set
- * @param effectiveCapabilities effective set
- * @throws IOException on error
- */
- static native void setCapabilities(
- long permittedCapabilities,
- long effectiveCapabilities) throws IOException;
-
- /**
* Invokes select() on the provider array of file descriptors (selecting
* for readability only). Array elements of null are ignored.
*
diff --git a/core/java/com/android/internal/util/FastXmlSerializer.java b/core/java/com/android/internal/util/FastXmlSerializer.java
index 592a8fa..7a04080 100644
--- a/core/java/com/android/internal/util/FastXmlSerializer.java
+++ b/core/java/com/android/internal/util/FastXmlSerializer.java
@@ -50,6 +50,8 @@
private static final int BUFFER_LEN = 8192;
+ private static String sSpace = " ";
+
private final char[] mText = new char[BUFFER_LEN];
private int mPos;
@@ -59,8 +61,12 @@
private CharsetEncoder mCharset;
private ByteBuffer mBytes = ByteBuffer.allocate(BUFFER_LEN);
+ private boolean mIndent = false;
private boolean mInTag;
+ private int mNesting = 0;
+ private boolean mLineStart = true;
+
private void append(char c) throws IOException {
int pos = mPos;
if (pos >= (BUFFER_LEN-1)) {
@@ -113,6 +119,14 @@
append(str, 0, str.length());
}
+ private void appendIndent(int indent) throws IOException {
+ indent *= 4;
+ if (indent > sSpace.length()) {
+ indent = sSpace.length();
+ }
+ append(sSpace, 0, indent);
+ }
+
private void escapeAndAppendString(final String string) throws IOException {
final int N = string.length();
final char NE = (char)ESCAPE_TABLE.length;
@@ -161,6 +175,7 @@
escapeAndAppendString(value);
append('"');
+ mLineStart = false;
return this;
}
@@ -185,9 +200,13 @@
public XmlSerializer endTag(String namespace, String name) throws IOException,
IllegalArgumentException, IllegalStateException {
+ mNesting--;
if (mInTag) {
append(" />\n");
} else {
+ if (mIndent && mLineStart) {
+ appendIndent(mNesting);
+ }
append("</");
if (namespace != null) {
append(namespace);
@@ -196,6 +215,7 @@
append(name);
append(">\n");
}
+ mLineStart = true;
mInTag = false;
return this;
}
@@ -278,6 +298,7 @@
public void setFeature(String name, boolean state) throws IllegalArgumentException,
IllegalStateException {
if (name.equals("http://xmlpull.org/v1/doc/features.html#indent-output")) {
+ mIndent = true;
return;
}
throw new UnsupportedOperationException();
@@ -325,6 +346,7 @@
IllegalArgumentException, IllegalStateException {
append("<?xml version='1.0' encoding='utf-8' standalone='"
+ (standalone ? "yes" : "no") + "' ?>\n");
+ mLineStart = true;
}
public XmlSerializer startTag(String namespace, String name) throws IOException,
@@ -332,6 +354,10 @@
if (mInTag) {
append(">\n");
}
+ if (mIndent) {
+ appendIndent(mNesting);
+ }
+ mNesting++;
append('<');
if (namespace != null) {
append(namespace);
@@ -339,6 +365,7 @@
}
append(name);
mInTag = true;
+ mLineStart = false;
return this;
}
@@ -349,6 +376,9 @@
mInTag = false;
}
escapeAndAppendString(buf, start, len);
+ if (mIndent) {
+ mLineStart = buf[start+len-1] == '\n';
+ }
return this;
}
@@ -359,6 +389,9 @@
mInTag = false;
}
escapeAndAppendString(text);
+ if (mIndent) {
+ mLineStart = text.length() > 0 && (text.charAt(text.length()-1) == '\n');
+ }
return this;
}
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index 1e5a97a..6641f2c 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -27,6 +27,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Calendar;
+import java.util.Collection;
import java.util.HashMap;
import java.util.Vector;
@@ -81,8 +82,8 @@
* machine will cause <code>haltedProcessMessage</code> to be invoked.</p>
*
* <p>If it is desirable to completely stop the state machine call <code>quit</code> or
- * <code>abort</code>. These will call <code>exit</code> of the current state and its parents, call
- * <code>onQuiting</code> and then exit Thread/Loopers.</p>
+ * <code>quitNow</code>. These will call <code>exit</code> of the current state and its parents,
+ * call <code>onQuiting</code> and then exit Thread/Loopers.</p>
*
* <p>In addition to <code>processMessage</code> each <code>State</code> has
* an <code>enter</code> method and <code>exit</exit> method which may be overridden.</p>
@@ -444,12 +445,13 @@
* {@hide}
*/
public static class LogRec {
+ private StateMachine mSm;
private long mTime;
private int mWhat;
private String mInfo;
- private State mState;
- private State mOrgState;
- private State mTransitionToState;
+ private IState mState;
+ private IState mOrgState;
+ private IState mDstState;
/**
* Constructor
@@ -461,26 +463,26 @@
* @param transToState is the state that was transitioned to after the message was
* processed.
*/
- LogRec(Message msg, String info, State state, State orgState, State transToState) {
- update(msg, info, state, orgState, transToState);
+ LogRec(StateMachine sm, Message msg, String info, IState state, IState orgState,
+ IState transToState) {
+ update(sm, msg, info, state, orgState, transToState);
}
/**
* Update the information in the record.
* @param state that handled the message
- * @param orgState is the first state the received the message but
- * did not processes the message.
- * @param transToState is the state that was transitioned to after the message was
- * processed.
+ * @param orgState is the first state the received the message
+ * @param dstState is the state that was the transition target when logging
*/
- public void update(Message msg, String info, State state, State orgState,
- State transToState) {
+ public void update(StateMachine sm, Message msg, String info, IState state, IState orgState,
+ IState dstState) {
+ mSm = sm;
mTime = System.currentTimeMillis();
mWhat = (msg != null) ? msg.what : 0;
mInfo = info;
mState = state;
mOrgState = orgState;
- mTransitionToState = transToState;
+ mDstState = dstState;
}
/**
@@ -507,21 +509,27 @@
/**
* @return the state that handled this message
*/
- public State getState() {
+ public IState getState() {
return mState;
}
/**
+ * @return the state destination state if a transition is occurring or null if none.
+ */
+ public IState getDestState() {
+ return mDstState;
+ }
+
+
+ /**
* @return the original state that received the message.
*/
- public State getOriginalState() {
+ public IState getOriginalState() {
return mOrgState;
}
- /**
- * @return as string
- */
- public String toString(StateMachine sm) {
+ @Override
+ public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("time=");
Calendar c = Calendar.getInstance();
@@ -532,9 +540,9 @@
sb.append(" org=");
sb.append(mOrgState == null ? "<null>" : mOrgState.getName());
sb.append(" dest=");
- sb.append(mTransitionToState == null ? "<null>" : mTransitionToState.getName());
+ sb.append(mDstState == null ? "<null>" : mDstState.getName());
sb.append(" what=");
- String what = sm.getWhatToString(mWhat);
+ String what = mSm != null ? mSm.getWhatToString(mWhat) : "";
if (TextUtils.isEmpty(what)) {
sb.append(mWhat);
sb.append("(0x");
@@ -647,18 +655,19 @@
* processed.
*
*/
- synchronized void add(Message msg, String messageInfo, State state, State orgState,
- State transToState) {
+ synchronized void add(StateMachine sm, Message msg, String messageInfo, IState state,
+ IState orgState,
+ IState transToState) {
mCount += 1;
if (mLogRecVector.size() < mMaxSize) {
- mLogRecVector.add(new LogRec(msg, messageInfo, state, orgState, transToState));
+ mLogRecVector.add(new LogRec(sm, msg, messageInfo, state, orgState, transToState));
} else {
LogRec pmi = mLogRecVector.get(mOldestIndex);
mOldestIndex += 1;
if (mOldestIndex >= mMaxSize) {
mOldestIndex = 0;
}
- pmi.update(msg, messageInfo, state, orgState, transToState);
+ pmi.update(sm, msg, messageInfo, state, orgState, transToState);
}
}
}
@@ -788,55 +797,79 @@
throw new RuntimeException("StateMachine.handleMessage: " +
"The start method not called, received msg: " + msg);
}
- performTransitions(msgProcessedState);
+ performTransitions(msgProcessedState, msg);
- if (mDbg) mSm.log("handleMessage: X");
+ // We need to check if mSm == null here as we could be quitting.
+ if (mDbg && mSm != null) mSm.log("handleMessage: X");
}
/**
* Do any transitions
* @param msgProcessedState is the state that processed the message
*/
- private void performTransitions(State msgProcessedState) {
+ private void performTransitions(State msgProcessedState, Message msg) {
/**
* If transitionTo has been called, exit and then enter
* the appropriate states. We loop on this to allow
* enter and exit methods to use transitionTo.
*/
- State destState = null;
State orgState = mStateStack[mStateStackTopIndex].state;
- /** Record whether message needs to be logged before transitions */
- boolean recordLogMsg = mSm.recordLogRec(mMsg);
+ /**
+ * Record whether message needs to be logged before we transition and
+ * and we won't log special messages SM_INIT_CMD or SM_QUIT_CMD which
+ * always set msg.obj to the handler.
+ */
+ boolean recordLogMsg = mSm.recordLogRec(mMsg) && (msg.obj == mSmHandlerObj);
- while (mDestState != null) {
- if (mDbg) mSm.log("handleMessage: new destination call exit");
+ if (mLogRecords.logOnlyTransitions()) {
+ /** Record only if there is a transition */
+ if (mDestState != null) {
+ mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState,
+ orgState, mDestState);
+ }
+ } else if (recordLogMsg) {
+ /** Record message */
+ mLogRecords.add(mSm, mMsg, mSm.getLogRecString(mMsg), msgProcessedState,
+ orgState, mDestState);
+ }
+ State destState = mDestState;
+ if (destState != null) {
/**
- * Save mDestState locally and set to null
- * to know if enter/exit use transitionTo.
+ * Process the transitions including transitions in the enter/exit methods
*/
- destState = mDestState;
+ while (true) {
+ if (mDbg) mSm.log("handleMessage: new destination call exit/enter");
+
+ /**
+ * Determine the states to exit and enter and return the
+ * common ancestor state of the enter/exit states. Then
+ * invoke the exit methods then the enter methods.
+ */
+ StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
+ invokeExitMethods(commonStateInfo);
+ int stateStackEnteringIndex = moveTempStateStackToStateStack();
+ invokeEnterMethods(stateStackEnteringIndex);
+
+
+ /**
+ * Since we have transitioned to a new state we need to have
+ * any deferred messages moved to the front of the message queue
+ * so they will be processed before any other messages in the
+ * message queue.
+ */
+ moveDeferredMessageAtFrontOfQueue();
+
+ if (destState != mDestState) {
+ // A new mDestState so continue looping
+ destState = mDestState;
+ } else {
+ // No change in mDestState so we're done
+ break;
+ }
+ }
mDestState = null;
-
- /**
- * Determine the states to exit and enter and return the
- * common ancestor state of the enter/exit states. Then
- * invoke the exit methods then the enter methods.
- */
- StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);
- invokeExitMethods(commonStateInfo);
- int stateStackEnteringIndex = moveTempStateStackToStateStack();
- invokeEnterMethods(stateStackEnteringIndex);
-
-
- /**
- * Since we have transitioned to a new state we need to have
- * any deferred messages moved to the front of the message queue
- * so they will be processed before any other messages in the
- * message queue.
- */
- moveDeferredMessageAtFrontOfQueue();
}
/**
@@ -859,21 +892,6 @@
mSm.onHalting();
}
}
-
- // Log only if state machine has not quit
- if (mSm != null) {
- if (mLogRecords.logOnlyTransitions()) {
- /** Record only if there is a transition */
- if (destState != null) {
- mLogRecords.add(mMsg, mSm.getLogRecString(mMsg), msgProcessedState,
- orgState, destState);
- }
- } else if (recordLogMsg) {
- /** Record message */
- mLogRecords.add(mMsg, mSm.getLogRecString(mMsg), msgProcessedState,
- orgState, destState);
- }
- }
}
/**
@@ -1255,20 +1273,6 @@
}
/**
- * @return current message
- */
- protected final Message getCurrentMessage() {
- return mSmHandler.getCurrentMessage();
- }
-
- /**
- * @return current state
- */
- protected final IState getCurrentState() {
- return mSmHandler.getCurrentState();
- }
-
- /**
* Add a new state to the state machine, parent will be null
* @param state to add
*/
@@ -1287,6 +1291,26 @@
}
/**
+ * @return current message
+ */
+ protected final Message getCurrentMessage() {
+ // mSmHandler can be null if the state machine has quit.
+ SmHandler smh = mSmHandler;
+ if (smh == null) return null;
+ return smh.getCurrentMessage();
+ }
+
+ /**
+ * @return current state
+ */
+ protected final IState getCurrentState() {
+ // mSmHandler can be null if the state machine has quit.
+ SmHandler smh = mSmHandler;
+ if (smh == null) return null;
+ return smh.getCurrentState();
+ }
+
+ /**
* transition to destination state. Upon returning
* from processMessage the current state's exit will
* be executed and upon the next message arriving
@@ -1390,21 +1414,44 @@
* @return number of log records
*/
public final int getLogRecSize() {
- return mSmHandler.mLogRecords.size();
+ // mSmHandler can be null if the state machine has quit.
+ SmHandler smh = mSmHandler;
+ if (smh == null) return 0;
+ return smh.mLogRecords.size();
}
/**
* @return the total number of records processed
*/
public final int getLogRecCount() {
- return mSmHandler.mLogRecords.count();
+ // mSmHandler can be null if the state machine has quit.
+ SmHandler smh = mSmHandler;
+ if (smh == null) return 0;
+ return smh.mLogRecords.count();
}
/**
- * @return a log record
+ * @return a log record, or null if index is out of range
*/
public final LogRec getLogRec(int index) {
- return mSmHandler.mLogRecords.get(index);
+ // mSmHandler can be null if the state machine has quit.
+ SmHandler smh = mSmHandler;
+ if (smh == null) return null;
+ return smh.mLogRecords.get(index);
+ }
+
+ /**
+ * @return a copy of LogRecs as a collection
+ */
+ public final Collection<LogRec> copyLogRecs() {
+ Vector<LogRec> vlr = new Vector<LogRec>();
+ SmHandler smh = mSmHandler;
+ if (smh != null) {
+ for (LogRec lr : smh.mLogRecords.mLogRecVector) {
+ vlr.add(lr);
+ }
+ }
+ return vlr;
}
/**
@@ -1413,17 +1460,11 @@
* @param string
*/
protected void addLogRec(String string) {
- mSmHandler.mLogRecords.add(null, string, null, null, null);
- }
-
- /**
- * Add the string and state to LogRecords
- *
- * @param string
- * @param state current state
- */
- protected void addLogRec(String string, State state) {
- mSmHandler.mLogRecords.add(null, string, state, null, null);
+ // mSmHandler can be null if the state machine has quit.
+ SmHandler smh = mSmHandler;
+ if (smh == null) return;
+ smh.mLogRecords.add(this, smh.getCurrentMessage(), string, smh.getCurrentState(),
+ smh.mStateStack[smh.mStateStackTopIndex].state, smh.mDestState);
}
/**
@@ -1452,168 +1493,217 @@
}
/**
- * @return Handler
+ * @return Handler, maybe null if state machine has quit.
*/
public final Handler getHandler() {
return mSmHandler;
}
/**
- * Get a message and set Message.target = this.
+ * Get a message and set Message.target state machine handler.
*
- * @return message or null if SM has quit
+ * Note: The handler can be null if the state machine has quit,
+ * which means target will be null and may cause a AndroidRuntimeException
+ * in MessageQueue#enqueMessage if sent directly or if sent using
+ * StateMachine#sendMessage the message will just be ignored.
+ *
+ * @return A Message object from the global pool
*/
public final Message obtainMessage()
{
- if (mSmHandler == null) return null;
-
return Message.obtain(mSmHandler);
}
/**
- * Get a message and set Message.target = this and what
+ * Get a message and set Message.target state machine handler, what.
+ *
+ * Note: The handler can be null if the state machine has quit,
+ * which means target will be null and may cause a AndroidRuntimeException
+ * in MessageQueue#enqueMessage if sent directly or if sent using
+ * StateMachine#sendMessage the message will just be ignored.
*
* @param what is the assigned to Message.what.
- * @return message or null if SM has quit
+ * @return A Message object from the global pool
*/
public final Message obtainMessage(int what) {
- if (mSmHandler == null) return null;
-
return Message.obtain(mSmHandler, what);
}
/**
- * Get a message and set Message.target = this,
+ * Get a message and set Message.target state machine handler,
* what and obj.
*
+ * Note: The handler can be null if the state machine has quit,
+ * which means target will be null and may cause a AndroidRuntimeException
+ * in MessageQueue#enqueMessage if sent directly or if sent using
+ * StateMachine#sendMessage the message will just be ignored.
+ *
* @param what is the assigned to Message.what.
* @param obj is assigned to Message.obj.
- * @return message or null if SM has quit
+ * @return A Message object from the global pool
*/
public final Message obtainMessage(int what, Object obj)
{
- if (mSmHandler == null) return null;
-
return Message.obtain(mSmHandler, what, obj);
}
/**
- * Get a message and set Message.target = this,
+ * Get a message and set Message.target state machine handler,
* what, arg1 and arg2
*
+ * Note: The handler can be null if the state machine has quit,
+ * which means target will be null and may cause a AndroidRuntimeException
+ * in MessageQueue#enqueMessage if sent directly or if sent using
+ * StateMachine#sendMessage the message will just be ignored.
+ *
* @param what is assigned to Message.what
* @param arg1 is assigned to Message.arg1
* @param arg2 is assigned to Message.arg2
- * @return A Message object from the global pool or null if
- * SM has quit
+ * @return A Message object from the global pool
*/
public final Message obtainMessage(int what, int arg1, int arg2)
{
- if (mSmHandler == null) return null;
-
return Message.obtain(mSmHandler, what, arg1, arg2);
}
/**
- * Get a message and set Message.target = this,
+ * Get a message and set Message.target state machine handler,
* what, arg1, arg2 and obj
*
+ * Note: The handler can be null if the state machine has quit,
+ * which means target will be null and may cause a AndroidRuntimeException
+ * in MessageQueue#enqueMessage if sent directly or if sent using
+ * StateMachine#sendMessage the message will just be ignored.
+ *
* @param what is assigned to Message.what
* @param arg1 is assigned to Message.arg1
* @param arg2 is assigned to Message.arg2
* @param obj is assigned to Message.obj
- * @return A Message object from the global pool or null if
- * SM has quit
+ * @return A Message object from the global pool
*/
public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
{
- if (mSmHandler == null) return null;
-
return Message.obtain(mSmHandler, what, arg1, arg2, obj);
}
/**
* Enqueue a message to this state machine.
+ *
+ * Message is ignored if state machine has quit.
*/
public final void sendMessage(int what) {
// mSmHandler can be null if the state machine has quit.
- if (mSmHandler == null) return;
+ SmHandler smh = mSmHandler;
+ if (smh == null) return;
- mSmHandler.sendMessage(obtainMessage(what));
+ smh.sendMessage(obtainMessage(what));
}
/**
* Enqueue a message to this state machine.
+ *
+ * Message is ignored if state machine has quit.
*/
public final void sendMessage(int what, Object obj) {
// mSmHandler can be null if the state machine has quit.
- if (mSmHandler == null) return;
+ SmHandler smh = mSmHandler;
+ if (smh == null) return;
- mSmHandler.sendMessage(obtainMessage(what,obj));
+ smh.sendMessage(obtainMessage(what,obj));
}
/**
* Enqueue a message to this state machine.
+ *
+ * Message is ignored if state machine has quit.
*/
public final void sendMessage(Message msg) {
// mSmHandler can be null if the state machine has quit.
- if (mSmHandler == null) return;
+ SmHandler smh = mSmHandler;
+ if (smh == null) return;
- mSmHandler.sendMessage(msg);
+ smh.sendMessage(msg);
}
/**
* Enqueue a message to this state machine after a delay.
+ *
+ * Message is ignored if state machine has quit.
*/
public final void sendMessageDelayed(int what, long delayMillis) {
// mSmHandler can be null if the state machine has quit.
- if (mSmHandler == null) return;
+ SmHandler smh = mSmHandler;
+ if (smh == null) return;
- mSmHandler.sendMessageDelayed(obtainMessage(what), delayMillis);
+ smh.sendMessageDelayed(obtainMessage(what), delayMillis);
}
/**
* Enqueue a message to this state machine after a delay.
+ *
+ * Message is ignored if state machine has quit.
*/
public final void sendMessageDelayed(int what, Object obj, long delayMillis) {
// mSmHandler can be null if the state machine has quit.
- if (mSmHandler == null) return;
+ SmHandler smh = mSmHandler;
+ if (smh == null) return;
- mSmHandler.sendMessageDelayed(obtainMessage(what, obj), delayMillis);
+ smh.sendMessageDelayed(obtainMessage(what, obj), delayMillis);
}
/**
* Enqueue a message to this state machine after a delay.
+ *
+ * Message is ignored if state machine has quit.
*/
public final void sendMessageDelayed(Message msg, long delayMillis) {
// mSmHandler can be null if the state machine has quit.
- if (mSmHandler == null) return;
+ SmHandler smh = mSmHandler;
+ if (smh == null) return;
- mSmHandler.sendMessageDelayed(msg, delayMillis);
+ smh.sendMessageDelayed(msg, delayMillis);
}
/**
* Enqueue a message to the front of the queue for this state machine.
* Protected, may only be called by instances of StateMachine.
+ *
+ * Message is ignored if state machine has quit.
*/
protected final void sendMessageAtFrontOfQueue(int what, Object obj) {
- mSmHandler.sendMessageAtFrontOfQueue(obtainMessage(what, obj));
+ // mSmHandler can be null if the state machine has quit.
+ SmHandler smh = mSmHandler;
+ if (smh == null) return;
+
+ smh.sendMessageAtFrontOfQueue(obtainMessage(what, obj));
}
/**
* Enqueue a message to the front of the queue for this state machine.
* Protected, may only be called by instances of StateMachine.
+ *
+ * Message is ignored if state machine has quit.
*/
protected final void sendMessageAtFrontOfQueue(int what) {
- mSmHandler.sendMessageAtFrontOfQueue(obtainMessage(what));
+ // mSmHandler can be null if the state machine has quit.
+ SmHandler smh = mSmHandler;
+ if (smh == null) return;
+
+ smh.sendMessageAtFrontOfQueue(obtainMessage(what));
}
/**
* Enqueue a message to the front of the queue for this state machine.
* Protected, may only be called by instances of StateMachine.
+ *
+ * Message is ignored if state machine has quit.
*/
protected final void sendMessageAtFrontOfQueue(Message msg) {
- mSmHandler.sendMessageAtFrontOfQueue(msg);
+ // mSmHandler can be null if the state machine has quit.
+ SmHandler smh = mSmHandler;
+ if (smh == null) return;
+
+ smh.sendMessageAtFrontOfQueue(msg);
}
/**
@@ -1621,7 +1711,23 @@
* Protected, may only be called by instances of StateMachine.
*/
protected final void removeMessages(int what) {
- mSmHandler.removeMessages(what);
+ // mSmHandler can be null if the state machine has quit.
+ SmHandler smh = mSmHandler;
+ if (smh == null) return;
+
+ smh.removeMessages(what);
+ }
+
+ /**
+ * Validate that the message was sent by
+ * {@link StateMachine#quit} or {@link StateMachine#quitNow}.
+ * */
+ protected final boolean isQuit(Message msg) {
+ // mSmHandler can be null if the state machine has quit.
+ SmHandler smh = mSmHandler;
+ if (smh == null) return msg.what == SM_QUIT_CMD;
+
+ return smh.isQuit(msg);
}
/**
@@ -1629,9 +1735,10 @@
*/
protected final void quit() {
// mSmHandler can be null if the state machine is already stopped.
- if (mSmHandler == null) return;
+ SmHandler smh = mSmHandler;
+ if (smh == null) return;
- mSmHandler.quit();
+ smh.quit();
}
/**
@@ -1639,9 +1746,10 @@
*/
protected final void quitNow() {
// mSmHandler can be null if the state machine is already stopped.
- if (mSmHandler == null) return;
+ SmHandler smh = mSmHandler;
+ if (smh == null) return;
- mSmHandler.quitNow();
+ smh.quitNow();
}
/**
@@ -1649,9 +1757,10 @@
*/
public boolean isDbg() {
// mSmHandler can be null if the state machine has quit.
- if (mSmHandler == null) return false;
+ SmHandler smh = mSmHandler;
+ if (smh == null) return false;
- return mSmHandler.isDbg();
+ return smh.isDbg();
}
/**
@@ -1661,9 +1770,10 @@
*/
public void setDbg(boolean dbg) {
// mSmHandler can be null if the state machine has quit.
- if (mSmHandler == null) return;
+ SmHandler smh = mSmHandler;
+ if (smh == null) return;
- mSmHandler.setDbg(dbg);
+ smh.setDbg(dbg);
}
/**
@@ -1671,10 +1781,11 @@
*/
public void start() {
// mSmHandler can be null if the state machine has quit.
- if (mSmHandler == null) return;
+ SmHandler smh = mSmHandler;
+ if (smh == null) return;
/** Send the complete construction message */
- mSmHandler.completeConstruction();
+ smh.completeConstruction();
}
/**
@@ -1688,7 +1799,7 @@
pw.println(getName() + ":");
pw.println(" total records=" + getLogRecCount());
for (int i=0; i < getLogRecSize(); i++) {
- pw.printf(" rec[%d]: %s\n", i, getLogRec(i).toString(this));
+ pw.printf(" rec[%d]: %s\n", i, getLogRec(i).toString());
pw.flush();
}
pw.println("curState=" + getCurrentState().getName());
diff --git a/core/jni/android_os_MessageQueue.cpp b/core/jni/android_os_MessageQueue.cpp
index a4dcac6..7540645 100644
--- a/core/jni/android_os_MessageQueue.cpp
+++ b/core/jni/android_os_MessageQueue.cpp
@@ -109,55 +109,34 @@
// ----------------------------------------------------------------------------
-static NativeMessageQueue* android_os_MessageQueue_getNativeMessageQueue(JNIEnv* env,
- jobject messageQueueObj) {
+sp<MessageQueue> android_os_MessageQueue_getMessageQueue(JNIEnv* env, jobject messageQueueObj) {
jint intPtr = env->GetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr);
return reinterpret_cast<NativeMessageQueue*>(intPtr);
}
-static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv* env, jobject messageQueueObj,
- NativeMessageQueue* nativeMessageQueue) {
- env->SetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr,
- reinterpret_cast<jint>(nativeMessageQueue));
-}
-
-sp<MessageQueue> android_os_MessageQueue_getMessageQueue(JNIEnv* env, jobject messageQueueObj) {
- NativeMessageQueue* nativeMessageQueue =
- android_os_MessageQueue_getNativeMessageQueue(env, messageQueueObj);
- return nativeMessageQueue;
-}
-
-static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) {
+static jint android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
- return;
+ return 0;
}
nativeMessageQueue->incStrong(env);
- android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue);
+ return reinterpret_cast<jint>(nativeMessageQueue);
}
-static void android_os_MessageQueue_nativeDestroy(JNIEnv* env, jobject obj) {
- NativeMessageQueue* nativeMessageQueue =
- android_os_MessageQueue_getNativeMessageQueue(env, obj);
- if (nativeMessageQueue) {
- android_os_MessageQueue_setNativeMessageQueue(env, obj, NULL);
- nativeMessageQueue->decStrong(env);
- }
+static void android_os_MessageQueue_nativeDestroy(JNIEnv* env, jclass clazz, jint ptr) {
+ NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
+ nativeMessageQueue->decStrong(env);
}
-static void throwQueueNotInitialized(JNIEnv* env) {
- jniThrowException(env, "java/lang/IllegalStateException", "Message queue not initialized");
-}
-
-static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
+static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jclass clazz,
jint ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, timeoutMillis);
}
-static void android_os_MessageQueue_nativeWake(JNIEnv* env, jobject obj, jint ptr) {
+static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jint ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
return nativeMessageQueue->wake();
}
@@ -166,8 +145,8 @@
static JNINativeMethod gMessageQueueMethods[] = {
/* name, signature, funcPtr */
- { "nativeInit", "()V", (void*)android_os_MessageQueue_nativeInit },
- { "nativeDestroy", "()V", (void*)android_os_MessageQueue_nativeDestroy },
+ { "nativeInit", "()I", (void*)android_os_MessageQueue_nativeInit },
+ { "nativeDestroy", "(I)V", (void*)android_os_MessageQueue_nativeDestroy },
{ "nativePollOnce", "(II)V", (void*)android_os_MessageQueue_nativePollOnce },
{ "nativeWake", "(I)V", (void*)android_os_MessageQueue_nativeWake }
};
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index de14826..f4fcc81 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -479,7 +479,7 @@
rects.push(r.fTop);
rects.push(r.fRight);
rects.push(r.fBottom);
- count++;
+ count += 4;
it.next();
}
renderer->drawRects(rects.array(), count, paint);
diff --git a/core/jni/com_android_internal_os_ZygoteInit.cpp b/core/jni/com_android_internal_os_ZygoteInit.cpp
index 7e5dede..bc8c4a7 100644
--- a/core/jni/com_android_internal_os_ZygoteInit.cpp
+++ b/core/jni/com_android_internal_os_ZygoteInit.cpp
@@ -163,32 +163,6 @@
}
}
-static void com_android_internal_os_ZygoteInit_setCapabilities (JNIEnv *env,
- jobject clazz, jlong permitted, jlong effective)
-{
- struct __user_cap_header_struct capheader;
- struct __user_cap_data_struct capdata;
- int err;
-
- memset (&capheader, 0, sizeof(capheader));
- memset (&capdata, 0, sizeof(capdata));
-
- capheader.version = _LINUX_CAPABILITY_VERSION;
- capheader.pid = 0;
-
- // As of this writing, capdata is __u32, but that's expected
- // to change...
- capdata.effective = effective;
- capdata.permitted = permitted;
-
- err = capset (&capheader, &capdata);
-
- if (err < 0) {
- jniThrowIOException(env, errno);
- return;
- }
-}
-
static jlong com_android_internal_os_ZygoteInit_capgetPermitted (JNIEnv *env,
jobject clazz, jint pid)
{
@@ -304,8 +278,6 @@
(void *) com_android_internal_os_ZygoteInit_reopenStdio},
{ "setCloseOnExec", "(Ljava/io/FileDescriptor;Z)V",
(void *) com_android_internal_os_ZygoteInit_setCloseOnExec},
- { "setCapabilities", "(JJ)V",
- (void *) com_android_internal_os_ZygoteInit_setCapabilities },
{ "capgetPermitted", "(I)J",
(void *) com_android_internal_os_ZygoteInit_capgetPermitted },
{ "selectReadable", "([Ljava/io/FileDescriptor;)I",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index aafc4bf..558080c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -690,6 +690,12 @@
android:permissionGroup="android.permission-group.NETWORK"
android:protectionLevel="signature|system" />
+ <!-- Allows access to the loop radio (Android@Home mesh network) device.
+ @hide -->
+ <permission android:name="android.permission.LOOP_RADIO"
+ android:permissionGroup="android.permission-group.NETWORK"
+ android:protectionLevel="signature|system" />
+
<!-- ================================== -->
<!-- Permissions for accessing accounts -->
<!-- ================================== -->
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index f0ad4bf..c4d456b 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"የእንቅስቃሴውን አደራጅ ወደ ዝጋ ሁነታ አስቀምጥ።ሙሉ ለሙሉ ዝጋ አያከናውንም።"</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"የትግበራ መቀያየርን ተከላከል"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"ተጠቃሚው ከሌላ መተግበሪያ ከመቀየር ይከላከላል።"</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"የአሁኑ የመተግበሪያ መረጃ ያግኙ"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"ያዢው በማያ ገጹ ፊት ላይ ስላለው የአሁኑ መተግበሪያ የግል መረጃ እንዲያመጣ ያስችለዋል።"</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"ሁሉንም መተግበሪያ ማስነሻ አሳይ እና ተቆጣጠር"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"እንቅስቃሴዎችን ስርዓቱ እንዴት እንደሚያስጀምር ለመከታተል እና ለመቆጣጠር ለመተግበሪያው ይፈቅዳሉ፡፡ ተንኮል አዘል መተግበሪያዎች የስርዓቱን ክብረ ገመና ሙሉለሙሉ ሊያጋልጡ ይችላሉ፡፡ ይህ ፍቃድ የሚያስፈልገው ለግንባታ ብቻ ነው፤ ለመደበኛ አጠቃቀም ፈጽሞ አይደለም፡፡"</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"አካታች የተወገደለት ስርጭት ላክ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index c2f5a35..39a31d5 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -783,7 +783,7 @@
<string name="lockscreen_transport_pause_description" msgid="7659088786780128001">"زر الإيقاف المؤقت"</string>
<string name="lockscreen_transport_play_description" msgid="5888422938351019426">"زر التشغيل"</string>
<string name="lockscreen_transport_stop_description" msgid="4562318378766987601">"زر الإيقاف"</string>
- <string name="emergency_calls_only" msgid="6733978304386365407">"مكالمات الطوارئ فقط"</string>
+ <string name="emergency_calls_only" msgid="6733978304386365407">"مكالمات طوارئ فقط"</string>
<string name="lockscreen_network_locked_message" msgid="143389224986028501">"الشبكة مؤمّنة"</string>
<string name="lockscreen_sim_puk_locked_message" msgid="7441797339976230">"بطاقة SIM مؤمّنة بكود PUK."</string>
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"راجع دليل المستخدم أو اتصل بخدمة العملاء."</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 0f1fda4..294b528 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Спыняе дзейнасць менеджэра. Не выконвае поўнае адключэнне."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"прадухіляць пераключэнне прыкладанняў"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Не дазваляе карыстальніку пераходзіць да іншага прыкладання."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"атрымаць бягучую інфармацыю прыкладання"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Дазваляе ўладальніку атрымлiваць асабістую інфармацыю аб бягучым прыкладаннi на пярэднім плане экрана."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"адсочваць і кантраляваць запуск усіх прыкладанняў"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Дазваляе прыкладанню сачыць і кантраляваць, як сістэма запускае працэсы. Шкоднасныя прыкладанні могуць цалкам парушыць працу сістэмы. Гэты дазвол патрэбны толькі для распрацоўкі, ніколі для звычайнага выкарыстання."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"трансляваць паведамленні аб выдаленні пакетаў"</string>
@@ -1458,7 +1456,7 @@
<string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Вы няправільна ўвялі графічны ключ разблакiроўкi пэўную колькасць разоў: <xliff:g id="NUMBER_0">%d</xliff:g>. Пасля яшчэ некалькiх няўдалых спроб (<xliff:g id="NUMBER_1">%d</xliff:g>) вам будзе прапанавана разблакiраваць тэлефон, увайшоўшы ў Google."\n\n" Паўтарыце спробу праз <xliff:g id="NUMBER_2">%d</xliff:g> с."</string>
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Вы няправільна ўвялі графічны ключ разблакiроўкi пэўную колькасць разоў: <xliff:g id="NUMBER_0">%d</xliff:g>. Пасля яшчэ некалькiх няўдалых спроб (<xliff:g id="NUMBER_1">%d</xliff:g>) вам будзе прапанавана разблакiраваць тэлефон, увайшоўшы ў Google."\n\n" Паўтарыце спробу праз <xliff:g id="NUMBER_2">%d</xliff:g> с."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
- <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Выдаліць"</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Выдалiць"</string>
<string name="safe_media_volume_warning" product="default" msgid="7382971871993371648">"Павялiчыць гук больш за рэкамендаваны ўзровень?"\n"Доўгае слуханне музыкi на вялiкай гучнасцi можа пашкодзiць ваш слых."</string>
<string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Утрымлiвайце два пальцы, каб уключыць доступ."</string>
<string name="accessibility_enabled" msgid="1381972048564547685">"Даступнасць уключана."</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 54b6198..6a4afa3 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Изключва диспечера на дейностите. Не извършва пълно изключване."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"предотвратяване на превключването между приложения"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Не позволява на потребителя да превключва към друго приложение."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"извличане на информация за текущото приложение"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Разрешава на собственика да извлича частна информация за текущото приложение на преден план на екрана."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"наблюдение и контрол на стартирането на всички приложения"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Разрешава на приложението да наблюдава и контролира как системата стартира дейности. Злонамерените приложения могат изцяло да компрометират системата. Това разрешение е нужно само за програмиране, никога за нормална употреба."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"изпращане на излъчване при премахнат пакет"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 5acb723..cf8192a 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Posa el gestor d\'activitats en estat d\'apagada. No fa una apagada completa."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"impedir els canvis d\'aplicació"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Impedeix que l\'usuari canviï a una altra aplicació."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"obtenció d\'informació de l\'aplicació actual"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Permet que el titular recuperi informació privada sobre l\'aplicació actual al primer pla de la pantalla."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"supervisa i controla tots els inicis d\'aplicacions"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Permet que l\'aplicació supervisi i controli com el sistema inicia activitats. Les aplicacions malicioses poden comprometre totalment el sistema. Aquest permís només és necessari per al desenvolupament, mai per a l\'ús normal."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"enviar difusió d\'eliminació de paquet"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index f10a217..2420ef2 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Uvede správce činností do vypnutého stavu. Nedojde však k úplnému vypnutí."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"zabránění přepínání aplikací"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Zabrání uživateli přepnout na jinou aplikaci."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"získat informace o aktuální aplikaci"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Umožňuje držiteli získat soukromé informace o aktuální aplikaci na popředí obrazovky."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"sledování a řízení spouštění všech aplikací"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Umožňuje aplikaci sledovat a řídit spouštění činností systémem. Škodlivé aplikace mohou systém zcela ovládnout. Toto oprávnění je požadováno pouze pro účely vývoje, nikdy pro běžné použití."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"odeslání vysílání o odstranění balíčku"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index eec3dd4..7a7035a 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Sætter aktivitetsadministratoren i lukningstilstand. Lukker ikke helt ned."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"undgå programskift"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Forhindrer brugeren i at skifte til en anden app."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"få aktuelle app-oplysninger"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Tillader, at brugeren henter private oplysninger om den aktuelle applikation i forgrunden på skærmen."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"overvåge og kontrollere åbning af alle apps"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Tillader, at appen kan overvåge og kontrollere, hvordan systemet starter aktiviteter. Ondsindede apps kan fuldstændig kompromittere systemet. Denne tilladelse er kun nødvendig til udvikling, aldrig til normal brug."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"send udsendelse om fjernet pakke"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index d5a67f0..d870d8a 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Versetzt den Aktivitätsmanager in einen heruntergefahrenen Zustand. Führt kein vollständiges Herunterfahren aus."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"App-Wechsel verhindern"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Hindert den Nutzer daran, zu einer anderen App zu wechseln"</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"Informationen zur aktuellen App abrufen"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Ermöglicht es dem Inhaber, private Informationen zur aktuellen App im Vordergrund des Bildschirms abzurufen"</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"Start von Apps überwachen und steuern"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Ermöglicht der App, den Start von Systemaktivitäten zu überwachen und zu steuern. Schädliche Apps können so das gesamte System beeinträchtigen. Diese Berechtigung wird nur zu Entwicklungszwecken und nie für die normale Nutzung benötigt."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"Broadcast ohne Paket senden"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index cf42a41..3bfcef5 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Θέτει το πρόγραμμα διαχείρισης δραστηριοτήτων σε κατάσταση τερματισμού λειτουργιών. Δεν εκτελεί πλήρη τερματισμό λειτουργιών."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"αποτροπή εναλλαγών εφαρμογών"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Δεν επιτρέπει στο χρήστη να μεταβεί σε άλλη εφαρμογή."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"λήψη πληροφοριών σχετικά με την τρέχουσα εφαρμογή"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Δίνει στον κάτοχο τη δυνατότητα ανάκτησης απόρρητων πληροφοριών σχετικά με την τρέχουσα εφαρμογή στο προσκήνιο της οθόνης."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"παρακολούθηση και έλεγχος όλων των εκκινήσεων εφαρμογών"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Επιτρέπει στην εφαρμογή να παρακολουθεί και να ελέγχει τον τρόπο με τον οποίο το σύστημα εκκινεί δραστηριότητες. Τυχόν κακόβουλες εφαρμογές ενδέχεται να θέσουν σε κίνδυνο το σύστημα. Αυτή η άδεια είναι απαραίτητη μόνο για σκοπούς ανάπτυξης και ποτέ για συνήθη χρήση."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"αποστολή εκπομπής χωρίς πακέτο"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 73ee358..368c814c 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Puts the activity manager into a shut-down state. Does not perform a complete shut down."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"prevent app switches"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Prevents the user from switching to another app."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"get current app info"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Allows the holder to retrieve private information about the current application in the foreground of the screen."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"monitor and control all app launching"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Allows the app to monitor and control how the system launches activities. Malicious apps may completely compromise the system. This permission is only needed for development, never for normal use."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"send package removed broadcast"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 6bb8e44..f36a03b 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Pone al administrador de la actividad en estado de cierre. No realiza un cierre completo."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"impedir conmutadores de aplicación"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Evita que el usuario cambie a otra aplicación."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"Obtener información de aplicación actual"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Permite que el titular recupere información privada sobre la aplicación actual en el primer plano de la pantalla."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"supervisar y controlar la ejecución de todas las aplicaciones"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Permite que la aplicación supervise y controle la manera en la que el sistema inicia actividades. Las aplicaciones maliciosas pueden comprometer el sistema por completo. Este permiso es necesario solo para el desarrollo, nunca para el uso habitual."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"enviar emisión de paquete eliminado"</string>
@@ -897,7 +895,7 @@
<string name="permlab_updateLock" msgid="3527558366616680889">"no realizar actualizaciones automáticas"</string>
<string name="permdesc_updateLock" msgid="1655625832166778492">"Permite a su propietario ofrecer información al sistema acerca de cuándo sería adecuado reiniciar el sistema de forma no interactiva y actualizar el dispositivo."</string>
<string name="save_password_message" msgid="767344687139195790">"¿Quieres recordar esta contraseña en el navegador?"</string>
- <string name="save_password_notnow" msgid="6389675316706699758">"Ahora no."</string>
+ <string name="save_password_notnow" msgid="6389675316706699758">"Ahora no"</string>
<string name="save_password_remember" msgid="6491879678996749466">"Recuerda"</string>
<string name="save_password_never" msgid="8274330296785855105">"Nunca"</string>
<string name="open_permission_deny" msgid="7374036708316629800">"No tienes permiso para abrir esta página."</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index fbbcf89..62ed107 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Pone el administrador de actividades en estado de cierre. No realiza un cierre completo."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"evitar cambios de aplicación"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Evita que el usuario cambie a otra aplicación."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"obtener información de la aplicación actual"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Permite que el titular recupere información privada sobre la aplicación actual en el primer plano de la pantalla."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"supervisar y controlar la ejecución de todas las aplicaciones"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Permite que la aplicación supervise y controle la ejecución de las actividades del sistema. Las aplicaciones malintencionadas pueden vulnerar la seguridad del sistema. Este permiso es necesario únicamente para tareas de desarrollo, nunca para el uso habitual."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"enviar emisión eliminada de paquete"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 00b5c2d..3141dab 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Lülitab tegevushalduri väljalülitusolekusse. Ei lülita lõplikult välja."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"väldi rakenduste ümberlülitamist"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Takistab kasutaja lülitumist teisele rakendusele."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"aktiivse rakenduse teabe hankimine"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Lubab õiguste saajal hankida privaatset teavet ekraanil esiplaanil oleva aktiivse rakenduse kohta."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"Kõigi rakenduste käivitumise jälgimine ja juhtimine"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Võimaldab rakendusel jälgida ja juhtida, kuidas süsteem tegevusi käivitab. Pahatahtlikud rakendused võivad süsteemi täielikult rikkuda. Seda õigust on vaja ainult arenduseks, mitte tavakasutuse korral."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"saada paketist eemaldatud saade"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index c4dfd6c..cd0b05d 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Asettaa toimintojen hallinnan sulkeutumistilaan. Ei sulje puhelinta kokonaan."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"estä sovellusten vaihto"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Estää käyttäjää siirtymästä toiseen sovellukseen."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"hae nykyisen sovelluksen tiedot"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Antaa sovellukselle luvan noutaa nykyistä sovellusta koskevia yksityisiä tietoja ruudun etualalla."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"kaikkien sovellusten käynnistämisen valvonta ja hallinta"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Antaa sovelluksen valvoa ja hallita sitä, miten laite käynnistää toimintoja. Haitalliset sovellukset voivat vaarantaa laitteen käytön. Tätä oikeutta tarvitaan vain kehityskäyttöön eikä koskaan tavalliseen käyttöön."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"lähetä paketeista poistettuja lähetyksiä"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index ea1ef50..420bbcf 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Place le gestionnaire d\'activités en état d\'arrêt. N\'effectue pas un arrêt complet."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"empêcher les changements d\'applications"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Empêche l\'utilisateur de changer d\'application."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"Récupérer des informations sur l\'application actuelle"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Permet à l\'application autorisée de récupérer des informations confidentielles à propos de l\'application exécutée au premier plan sur l\'écran."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"suivre et contrôler le lancement de toutes les applications"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Permet à l\'application de surveiller et de contrôler la façon dont le système lance les activités. Des applications malveillantes peuvent exploiter cette fonctionnalité pour totalement compromettre le système. Cette autorisation est uniquement destinée aux développeurs. Elle ne doit jamais être activée dans le cadre d\'une utilisation standard."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"Envoyer une diffusion sans paquet"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index c3ef672..2a08254 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"गतिविधि प्रबंधक को शटडाउन स्थिति में रखता है. पूर्ण शटडाउन निष्पादित नहीं करता है."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"एप्लिकेशन स्विच करने से रोकता है"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"उपयोगकर्ता को दूसरे एप्लिकेशन पर स्विच करने से रोकता है."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"वर्तमान एप्लिकेशन की जानकारी प्राप्त करें"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"धारक को स्क्रीन के अग्रभाग में स्थित वर्तमान एप्लिकेशन के बारे में निजी जानकारी प्राप्त करने देती है."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"सभी एप्लिकेशन की लॉन्चिंग की निगरानी करें और उसे नियंत्रित करें"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"एप्लिकेशन को यह निगरानी और नियंत्रित करने देता है कि सिस्टम कैसे गतिविधियां लॉन्च करता है. दुर्भावनापूर्ण एप्लिकेशन सिस्टम को पूरी तरह से जोखिम में डाल सकते हैं. इस अनुमति की आवश्यकता केवल विकास के लिए है, सामान्य उपयोग के लिए कभी नहीं."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"पैकेज निकाले गए प्रसारण भेजें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 72aad27..607afdf 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Postavlja upravitelja za aktivnost u stanje mirovanja. Ne isključuje ga u potpunosti."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"sprečavanje promjene aplikacije"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Sprječava korisnika u prebacivanju na drugu aplikaciju."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"dohvaćanje informacija o trenutačnoj aplikaciji"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Omogućuje nositelju dohvaćanje privatnih informacija o trenutačnoj aplikaciji u prednjem planu na zaslonu."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"praćenje i nadzor svih pokretanja aplikacija"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Omogućuje aplikaciji nadzor i upravljanje načinom na koji sustav pokreće aktivnosti. Zlonamjerne aplikacije mogu posve ugroziti sustav. Ta je dozvola potrebna samo za razvoj, nikada za uobičajenu upotrebu."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"slanje paketno uklonjenog prijenosa"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 6dd4f8f..cca48c8 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Leállítás állapotba helyezi a tevékenységkezelőt. Nem hajtja végre a teljes leállítást."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"alkalmazásváltás megakadályozása"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Megakadályozza, hogy a felhasználó átváltson egy másik alkalmazásra."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"az alkalmazás aktuális információinak lekérése"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Lehetővé teszi, hogy a felhasználó privát adatokat kérjen le az aktuális alkalmazásról a képernyő előterében."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"alkalmazásindítások nyomon követése és vezérlése"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Lehetővé teszi az alkalmazás számára, hogy figyelje és vezérelje, hogy a rendszer hogyan indít el tevékenységeket. A rosszindulatú alkalmazások teljesen tönkretehetik a rendszert. Ez az engedély csak fejlesztéshez szükséges, normál használathoz sosem."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"eltávolított csomagú üzenetek küldése"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 8d3c139..1fdc861 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Letakkan pengelola aktivitas dalam kondisi mati. Tidak melakukan penonaktifan penuh."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"cegah pergantian aplikasi"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Mencegah pengguna beralih ke apl lain."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"dapatkan info tentang aplikasi yang aktif"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Memungkinkan pemegang mengambil informasi pribadi tentang aplikasi yang aktif di latar depan layar."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"memantau dan mengontrol semua peluncuran apl"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Mengizinkan apl memantau dan mengontrol cara sistem meluncurkan kegiatan. Apl berbahaya dapat meretas sistem sepenuhnya. Izin ini hanya diperlukan untuk pengembangan, tidak pernah diperlukan untuk penggunaan normal."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"kirim siaran paket dihapus"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 7fc8b73..73a743e 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Mette il gestore delle attività in uno stato di chiusura. Non esegue una chiusura completa."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"impedire commutazione applicazione"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Impedisce all\'utente di passare a un\'altra applicazione."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"recupero di informazioni sull\'app corrente"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Consente al titolare di recuperare le informazioni private sull\'app correntemente in primo piano sullo schermo."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"monitoraggio e controllo avvio di tutte le applicazioni"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Consente all\'applicazione di monitorare e controllare l\'avvio delle attività da parte del sistema. Le applicazioni dannose potrebbero compromettere completamente il sistema. Questa autorizzazione è necessaria solo per lo sviluppo, mai per l\'utilizzo normale."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"invio broadcast rimossi dal pacchetto"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 0afcbf7..5115890 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"アクティビティマネージャをシャットダウン状態にします。完全なシャットダウンは実行しません。"</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"アプリケーションの切り替えを禁止する"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"ユーザーが別のアプリに切り替えられないようにします。"</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"現在のアプリ情報の取得"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"画面のフォアグラウンドで現在のアプリに関する非公開情報を取得することを所有者に許可します。"</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"すべてのアプリ起動の監視と制御"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"システムによるアクティビティ起動方法を監視し制御することをアプリに許可します。この許可を悪意のあるアプリに利用されると、システム全体のセキュリティが侵害される恐れがあります。この許可は開発時にのみ必要で、通常の使用では不要です。"</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"パッケージ削除ブロードキャストの送信"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 85b35a7..9bac0d5 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"작업 관리자를 종료 상태로 설정합니다. 전체 종료를 수행하지는 않습니다."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"애플리케이션 전환 방지"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"사용자가 다른 앱으로 전환하지 못하게 합니다."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"현재 앱 정보 얻기"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"권한을 가진 프로그램이 화면의 포그라운드에서 현재 애플리케이션에 대한 비공개 정보를 검색하도록 허용합니다."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"실행 중인 모든 앱 모니터링 및 제어"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"앱이 시스템에서 활동이 시작되는 방식을 모니터링하고 관리할 수 있도록 허용합니다. 이 경우 악성 앱이 이 기능을 이용하여 시스템을 완전히 손상시킬 수 있습니다. 이 권한은 개발 과정에만 필요하며 일반 사용 시에는 필요하지 않습니다."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"패키지 제거 브로드캐스트 보내기"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 86262e9..9ad75b3 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Veiklos tvarkyklę perjungia į išsijungimo būseną. Neišjungia visiškai."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"neleisti perjungti programų"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Neleidžiama naudotojui perjungti į kitą programą."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"gauti esamos programos informaciją"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Savininkui leidžiama gauti privačią esamos pirmaeilės ekrano programos informaciją."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"stebėti ir valdyti visų programų paleidimą"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Leidžiama programai stebėti ir valdyti, kaip sistema paleidžia veiklą. Kenkėjiškos programos gali visiškai pažeisti sistemą. Šis leidimas reikalingas tik kuriant ir jo niekada nereikia naudojant įprastai."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"siųsti pašalinto paketo perdavimą"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index cd28e58..71f4b41 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Liek darbību pārvaldniekam pāriet izslēgšanas stāvoklī. Neveic pilnīgu izslēgšanu."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"novērst lietojumprogrammu pārslēgšanu"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Novērš lietotāja pārslēgšanos uz citu lietotni."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"pašreizējās lietotnes informācijas iegūšana"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Ļauj īpašniekam izgūt privātu informāciju par pašreizējo lietojumprogrammu ekrāna priekšplānā."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"pārraudzīt un kontrolēt visu lietotņu atvēršanu"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Ļauj lietotnei pārraudzīt un kontrolēt, kā sistēmā tiek palaistas darbības. Ļaunprātīgas lietotnes var pilnībā uzlauzt sistēmu. Šī atļauja ir nepieciešama tikai izstrādei, taču ne parastai lietošanai."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"sūtīt apraidi par pakotnes noņemšanu"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 0969c34..9ad7a50 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Lar applikasjonen sette aktivitetshåndtereren i avslutningstilstand. Slår ikke systemet helt av."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"forhindre applikasjonsbytte"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Hindrer brukeren i å bytte til en annen app."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"få informasjon om appen"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Tillater brukeren å innhente privat informasjon om den aktuelle appen i forgrunnen på skjermen."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"avervåke og kontrollere all oppstart av apper"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Lar appen overvåke og kontrollere hvordan systemet starter opp aktiviteter. Ondsinnede apper kan utsette hele systemet for sikkerhetsbrudd. Denne tillatelsen er bare nødvendig for utviklere, aldri for vanlig bruk."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"kringkaste melding om fjernet pakke"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index f82e5dc..272f9c4 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Hiermee wordt activiteitenbeheer uitgeschakeld. Er wordt geen volledige uitschakeling uitgevoerd."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"schakelen tussen apps voorkomen"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Hiermee wordt voorkomen dat de gebruiker overschakelt naar een andere app."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"huidige appgegevens ophalen"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"De houder kan hiermee persoonlijke gegevens ophalen over de applicatie die momenteel op de voorgrond wordt weergegeven."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"alle startende apps bijhouden en beheren"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Hiermee kan de app de manier bijhouden en beheren waarop het systeem activiteiten start. Schadelijke apps kunnen het systeem volledig in gevaar brengen. Deze machtiging is alleen voor ontwikkeling vereist, nooit voor normaal gebruik."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"melding verzenden dat pakket is verwijderd"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 749a73e..6818152 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Przełącza menedżera aktywności w stan wyłączenia. Nie wykonuje pełnego wyłączenia."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"zapobieganie przełączaniu aplikacji"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Uniemożliwia użytkownikowi przełączenie na inną aplikację."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"pobierz informacje o bieżącej aplikacji"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Zezwala posiadaczowi na pobieranie prywatnych informacji o bieżącej aplikacji i wyświetlanie ich na pierwszym planie ekranu."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"monitorowanie i kontrolowanie wszystkich uruchamianych aplikacji"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Pozwala aplikacji na monitorowanie i kontrolowanie sposobu uruchamiania działań przez system. Złośliwe aplikacje mogą całkowicie naruszyć zabezpieczenia systemu. To uprawnienie nigdy nie jest potrzebne podczas normalnego użytkowania, a jedynie podczas programowania."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"wysyłanie transmisji informującej o usuniętym pakiecie"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 09a3d1f..1b42240 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Coloca o gestor de actividade num estado de encerramento. Não executa um encerramento completo."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"impedir trocas de aplicações"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Impede que o utilizador mude para outra aplicação."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"obter informações da aplicação atual"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Permite ao titular recuperar informações privadas acerca da aplicação atual no primeiro plano do ecrã."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"monitorizar e controlar a iniciação de todas as aplicações"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Permite que uma aplicação monitorize e controle a forma como o sistema inicia atividades. As aplicações maliciosas podem comprometer totalmente o sistema. Esta autorização só é necessária para programação, nunca para utilização normal."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"enviar difusão de pacote removido"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 3d5f68e..ba6c0aa 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Coloca o gerenciador de atividades em um estado de desligamento. Não executa o desligamento completo."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"evitar trocas de aplicativo"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Impede que o usuário alterne para outro aplicativo."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"obter informações do aplicativo atual"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Permite ao titular recuperar informações particulares sobre o aplicativo atual em primeiro plano na tela."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"monitorar e controlar todos os aplicativos que estão sendo iniciados"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Permite que o aplicativo monitore e controle a forma como o sistema inicia atividades. Aplicativos maliciosos podem comprometer completamente o sistema. Esta permissão só é necessária para o desenvolvimento, nunca para o uso normal."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"enviar transmissão removida do pacote"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index ecfef60..1892bfc 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Plasează Managerul de activităţi într-o stare de închidere. Nu efectuează o închidere completă."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"împiedicare comutare între aplicaţii"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Împiedică trecerea utilizatorului la o altă aplicaţie."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"obținere informații despre aplicația curentă"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Permite proprietarului să preia informațiile private despre aplicația curentă în prim-planul ecranului."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"monitorizare şi control asupra lansării tuturor aplicaţiilor"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Permite aplicaţiei să monitorizeze şi să controleze modul în care sistemul lansează activităţi. Aplicaţiile rău intenţionate pot să compromită sistemul în întregime. Această permisiune este necesară doar pentru dezvoltare şi niciodată pentru utilizarea normală."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"trimitere mesaj difuzat privind extragerea din pachet"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index f2908f4..6ac3ea4 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Завершает работу диспетчера активности. Не выполняет полное завершение работы."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"запретить переключение приложений"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Запрещает пользователям переключаться между приложениями."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"показывать информацию о текущем приложении"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"На экране будут отображаться сведения о текущем приложении."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"отслеживание и управление запуском всех приложений"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Приложение сможет отслеживать запуск системных процессов и управлять им. Вредоносные программы смогут получить полный контроль над системой. Это разрешение необходимо только для разработки и не нужно в обычном режиме."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"отправлять рассылку об удалении пакета"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 9f6702b..170aae2 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Uvedie správcu činností do vypnutého stavu. Úplné vypnutie však nenastane."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"zabrániť prepínaniu aplikácií"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Zabráni používateľovi prepnúť na inú aplikáciu."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"získať informácie o aktuálnej aplikácii"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Umožňuje držiteľovi povolenia načítať súkromné informácie o aktuálnej aplikácii v popredí obrazovky."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"sledovať a ovládať všetky spustenia aplikácií"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Umožňuje aplikácii sledovať a ovládať spúšťanie aktivít systémom. Škodlivé aplikácie môžu systém úplne ovládnuť. Toto povolenie je potrebné len na účely vývoja, nikdy nie na bežné používanie."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"odoslanie vysielania o odstránení balíčka"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 20e23ce..07e96e0 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Upravitelja dejavnosti preklopi v stanje za zaustavitev. Ne izvede celotne zaustavitve."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"preprečevanje preklopa programov"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Uporabniku preprečuje preklop v drug program."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"pridobivanje podatkov o trenutni aplikaciji"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Omogoča imetniku pridobivanje zasebnih podatkov o trenutni aplikaciji v ospredju zaslona."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"spremljanje in nadzor vseh zagonov programov"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Programu omogoča spremljanje in nadziranje načina, kako sistem zažene dejavnosti. Zlonamerni programi lahko v celoti ogrozijo varnost sistema. To dovoljenje je potrebno samo za razvoj, vendar nikoli za običajno uporabo."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"pošiljanje oddaje brez paketa"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index b4f2096..e8b3cbd 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Ставља менаџера активности у стање искључивања. Не искључује га у потпуности."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"спречавање пребацивања са једне апликације на другу"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Спречава да корисник пређе на другу апликацију."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"преузимање информација о актуелној апликацији"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Дозвољава власнику да преузима приватне информације о актуелној апликацији у првом плану екрана."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"надгледање и контрола покретања свих апликација"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Дозвољава апликацији да прати начин на који систем покреће активности и да њиме управља. Злонамерне апликације могу у потпуности да угрозе систем. Ова дозвола је потребна само за програмирање, а никада за уобичајено коришћење."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"слање емитовања уклоњеног пакета"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index f858d54..6bbd38f 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Sätter aktivitetshanteraren i avstängningsläge. Utför inte en fullständig avstängning."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"förhindrar programbyten"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Hindrar användaren från att byta till en annan app."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"hämta information om aktuell app"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Innehavaren tillåts att hämta privat information om den app som för tillfället är i förgrunden på skärmen."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"övervaka och styra alla appar som öppnas"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Tillåter att appen övervakar och styr hur systemet startar aktiviteter. Skadliga appar kan kompromettera systemet helt. Den här behörigheten behövs bara för programmering, aldrig för vanlig användning."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"skicka meddelande om borttaget paket"</string>
@@ -595,7 +593,7 @@
<string name="permlab_sdcardRead" product="default" msgid="8235341515605559677">"testa åtkomst till skyddad lagringsenhet"</string>
<string name="permdesc_sdcardRead" product="nosdcard" msgid="3642473292348132072">"Tillåter att appen testar behörighet till USB-enheter för användning på framtida enheter."</string>
<string name="permdesc_sdcardRead" product="default" msgid="5914402684685848828">"Tillåter appen att testa behörighet till SD-kortet för användning på framtida enheter."</string>
- <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"ändra eller ta bort innehållet"</string>
+ <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"ändra eller ta bort innehåll på USB-enheten"</string>
<string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"ändra eller ta bort innehåll på SD-kortet"</string>
<string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"Gör att app skriver till USB."</string>
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Tillåter att appen skriver till SD-kortet."</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 4430ed5..4070ac0 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Huweka kisimamia shughuli katika hali ya kuzima. Haiadhiri uzimaji kamili"</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"zuia swichi za app"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Huzuia mtumiaji dhidi ya kubadilisha na kwenda kwa programu nyingine."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"pata maelezo ya sasa kuhusu programu"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Huruhusu mmiliki kurejesha maelezo ya faragha kuhusu programu ya sasa katika mandharimbele ya skrini."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"Fuatilia na kudhibiti uzinduzi wote wa programu"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Inaruhusu programu kufuatilia na kudhibiti jinsi mfumo unazindua shughuli. Programu hasidi zinaweza kutia mfumo hatarini. Ruhusa inahitajika tu kwa usanidi, kamwe sio kwa matumizi ya kawaida."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"furushi lililotumwa limeondoa tangazo"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 1cad285..1e0e266 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"กำหนดให้ตัวจัดการกิจกรรมอยู่ในสถานะปิดระบบ โดยไม่ได้ปิดระบบอย่างสมบูรณ์"</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"ป้องกันการเปลี่ยนแอปพลิเคชัน"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"ป้องกันไม่ให้ผู้ใช้สลับไปใช้แอปพลิเคชันอื่น"</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"รับข้อมูลแอปพลิเคชันปัจจุบัน"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"ช่วยให้เจ้าของสามารถดึงข้อมูลส่วนตัวเกี่ยวกับแอปพลิเคชันปัจจุบันในส่วนหน้าของหน้าจอ"</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"ตรวจสอบและควบคุมแอปพลิเคชันทั้งหมดที่เปิดใช้งาน"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"อนุญาตให้แอปพลิเคชันตรวจสอบและควบคุมวิธีการที่ระบบเปิดกิจกรรมต่างๆ แอปพลิเคชันที่เป็นอันตรายอาจทำอันตรายแก่ระบบได้อย่างสิ้นเชิง การอนุญาตนี้จำเป็นสำหรับการพัฒนาเท่านั้น ไม่ใช้สำหรับแอปพลิเคชันทั่วไปโดยเด็ดขาด"</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"ส่งการกระจายข้อมูลว่ามีการนำแพคเกจออก"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 6798be9..7f4de43 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Inilalagay ang tagapamahala ng aktibidad sa katayuan ng pag-shutdown. Hindi nagsasagawa ng kumpletong pag-shutdown."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"pigilan ang mga paglipat ng app"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Pinipigilan ang mga user sa paglipat sa isa pang app."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"kunin ang impormasyon ng kasalukuyang app"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Pinapayagan ang may-ari na bawiin ang pribadong impormasyon tungkol sa kasalukuyang application sa foreground ng screen."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"subaybayan at kontrolin ang lahat ng paglunsad ng app"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Pinapayagan ang app na subaybayan at kontrolin kung paano naglulunsad ng mga aktibidad ang system. Maaaring ganap na ikompromiso ng nakakahamak na apps ang system. Kinakailangan lamang ang pahintulot na ito para sa pagpapabuti, hindi kailanman para sa normal na paggamit."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"magpadala ng package inalis ang broadcast"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 015fe1b..f79fbf5 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Eylem yöneticisini kapalı duruma getirir. Tam kapatma işlemi gerçekleştirmez."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"uygulama değişimlerini engelle"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Kullanıcının başka bir uygulamaya geçiş yapmasını engeller."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"geçerli uygulama bilgilerini al"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"İzin sahibine, ekranın ön planındaki geçerli uygulama hakkında gizli bilgileri alma olanağı verir."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"tüm uygulama başlatma işlemlerini izle ve denetle"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Uygulamaya, sistemin etkinlikleri nasıl başlattığını izleme ve denetleme izni verir. Kötü amaçlı uygulamalar sistemi tamamen tehlikeye atabilir. Bu izin normal kullanım için değildir, sadece geliştirme süreçlerinde kullanılır."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"paket ile kaldırılan yayını gönder"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 17f545c..0a5c65a 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Переводить диспетчер дій у стан завершення роботи. Не виконує повне завершення роботи."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"запобіг. зміні програм"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Запобігати переходу користувача до іншої програми."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"отримати інформацію про поточну програму"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Дозволяє власникові отримувати приватну інформацію про поточну програму на передньому плані екрана."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"відстежувати та контролювати запуски всіх програм"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Дозволяє програмі відстежувати та контролювати, як саме система запускає дії. Шкідливі програми можуть отримати повний контроль над системою. Цей дозвіл потрібний лише для розробки, а не для звичайного користування."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"надсил. запис про видал. пакета"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index d26ee14..d65fea2 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Đặt trình quản lý hoạt động sang trạng thái tắt. Không thực hiện tắt hoàn toàn."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"ngăn chuyển đổi ứng dụng"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Ngăn người dùng chuyển sang ứng dụng khác."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"truy cập thông tin ứng dụng hiện tại"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Cho phép chủ sở hữu truy xuất thông tin cá nhân về ứng dụng hiện tại ở nền trước của màn hình."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"giám sát và kiểm soát tất cả hoạt động khởi chạy ứng dụng"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Cho phép ứng dụng giám sát và kiểm soát cách hệ thống khởi chạy các hoạt động. Ứng dụng độc hại hoàn toàn có thể làm tổn hại hệ thống. Quyền này chỉ cần cho mục đích phát triển, không dành cho mục đích sử dụng thông thường."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"gửi truyền phát đã xóa của gói"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 87faf15..9fe3783 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"使活动管理器进入关闭状态。不执行彻底关机。"</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"禁止切换应用"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"阻止用户切换到其他应用。"</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"获取目前的应用信息"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"允许应用针对目前在屏幕前台运行的应用检索相关隐私信息。"</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"监控所有应用的启动"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"允许应用监视和控制系统是如何启动活动的。恶意应用可能会完全破坏系统。此权限只有在进行开发时才需要,正常使用情况下绝不需要。"</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"发送包删除的广播"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 6d1f3bd..dab5091 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"讓活動管理員進入關機狀態,而不執行完整的關機程序。"</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"防止切換應用程式"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"防止使用者切換到其他應用程式。"</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"取得目前的應用程式資訊"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"允許應用程式針對目前在螢幕前景運作的應用程式擷取私人資訊。"</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"監視及控制所有應用程式的啟動程序"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"允許應用程式監視和控制系統啟動活動的方式。請注意,惡意應用程式可能利用此功能破壞整個系統。這個權限只有開發人員才需要,一般使用者不需使用這個權限。"</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"傳送程式已移除廣播"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index dafec7c..ece5e2a 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -299,10 +299,8 @@
<string name="permdesc_shutdown" msgid="7046500838746291775">"Ibeka imeneja yomsebenzi kwisimo sokuvala shaqa. Ayenzi ukuvala shaqa okuphelele."</string>
<string name="permlab_stopAppSwitches" msgid="4138608610717425573">"gwema ukushintsha kohlelo lokusebenza"</string>
<string name="permdesc_stopAppSwitches" msgid="8262195802582255021">"Igwema umsebenzisi ukuthi ashintshele kolunye uhlelo lokusebenza."</string>
- <!-- no translation found for permlab_getTopActivityInfo (2537922311411546016) -->
- <skip />
- <!-- no translation found for permdesc_getTopActivityInfo (2512448855496067131) -->
- <skip />
+ <string name="permlab_getTopActivityInfo" msgid="2537922311411546016">"thola ulwazi lohlelo lokusebenza lwamanje"</string>
+ <string name="permdesc_getTopActivityInfo" msgid="2512448855496067131">"Ivumela umphathi ukuthi athole ulwazi oluyimfihlo mayelana nohlelo lokusebenza lwamanje ngaphambili kwesikrini."</string>
<string name="permlab_runSetActivityWatcher" msgid="892239094867182656">"qapha futhi ulawule ukuqaliswa kwazo zonke izinsiza"</string>
<string name="permdesc_runSetActivityWatcher" msgid="6003603162578577406">"Ivumela insiza ukuthi ihlole futhi ilawule ukuthi isistimu iziqalisa kanjani izehlakalo. Izinzisa ezinobungozi zingensa isistimu ibe sebungozini. Lemvume idingakalela intuthuku kuphela hhay ukusetshenziswa okwejwayelekile."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"thumela iphakheji yomsakazo okhishiwe"</string>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 41f8536..f8b26bc 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1241,13 +1241,6 @@
</intent-filter>
</activity>
- <activity android:name="android.accessibilityservice.InterrogationActivity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
- </intent-filter>
- </activity>
-
<activity android:name="android.animation.BasicAnimatorActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivity.java b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivity.java
deleted file mode 100644
index a9f144b..0000000
--- a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivity.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/**
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
- * in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
- * express or implied. See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.accessibilityservice;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.View;
-
-import com.android.frameworks.coretests.R;
-
-/**
- * Activity for testing the accessibility APIs for "interrogation" of
- * the screen content. These APIs allow exploring the screen and
- * requesting an action to be performed on a given view from an
- * AccessiiblityService.
- */
-public class InterrogationActivity extends Activity {
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.interrogation_activity);
-
- findViewById(R.id.button5).setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- /* do nothing */
- }
- });
- findViewById(R.id.button5).setOnLongClickListener(new View.OnLongClickListener() {
- public boolean onLongClick(View v) {
- return true;
- }
- });
- }
-}
diff --git a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java
deleted file mode 100644
index 3dc140b..0000000
--- a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java
+++ /dev/null
@@ -1,472 +0,0 @@
-/**
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
- * in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
- * express or implied. See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.accessibilityservice;
-
-import static android.view.accessibility.AccessibilityNodeInfo.ACTION_FOCUS;
-import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_FOCUS;
-import static android.view.accessibility.AccessibilityNodeInfo.ACTION_SELECT;
-import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_SELECTION;
-
-import android.graphics.Rect;
-import android.os.SystemClock;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.util.Log;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
-
-import com.android.frameworks.coretests.R;
-import com.android.internal.util.Predicate;
-
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Queue;
-
-/**
- * Activity for testing the accessibility APIs for "interrogation" of
- * the screen content. These APIs allow exploring the screen and
- * requesting an action to be performed on a given view from an
- * AccessiiblityService.
- */
-public class InterrogationActivityTest
- extends ActivityInstrumentationTestCase2<InterrogationActivity> {
- private static final boolean DEBUG = false;
-
- private static String LOG_TAG = "InterrogationActivityTest";
-
- // Timeout for the accessibility state of an Activity to be fully initialized.
- private static final int TIMEOUT_PROPAGATE_ACCESSIBILITY_EVENT_MILLIS = 5000;
-
- // Timeout for which non getting accessibility events considers the app idle.
- private static final long IDLE_EVENT_TIME_DELTA_MILLIS = 200;
-
- // Timeout in which to wait for idle device.
- private static final long GLOBAL_IDLE_DETECTION_TIMEOUT_MILLIS = 1000;
-
- // Handle to a connection to the AccessibilityManagerService
- private UiTestAutomationBridge mUiTestAutomationBridge;
-
- public InterrogationActivityTest() {
- super(InterrogationActivity.class);
- }
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- mUiTestAutomationBridge = new UiTestAutomationBridge();
- mUiTestAutomationBridge.connect();
- mUiTestAutomationBridge.waitForIdle(IDLE_EVENT_TIME_DELTA_MILLIS,
- GLOBAL_IDLE_DETECTION_TIMEOUT_MILLIS);
- mUiTestAutomationBridge.executeCommandAndWaitForAccessibilityEvent(new Runnable() {
- // wait for the first accessibility event
- @Override
- public void run() {
- // bring up the activity
- getActivity();
- }
- },
- new Predicate<AccessibilityEvent>() {
- @Override
- public boolean apply(AccessibilityEvent event) {
- return (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
- && event.getPackageName().equals(getActivity().getPackageName()));
- }
- },
- TIMEOUT_PROPAGATE_ACCESSIBILITY_EVENT_MILLIS);
- }
-
- @Override
- public void tearDown() throws Exception {
- mUiTestAutomationBridge.disconnect();
- super.tearDown();
- }
-
- @LargeTest
- public void testFindAccessibilityNodeInfoByViewId() throws Exception {
- final long startTimeMillis = SystemClock.uptimeMillis();
- try {
- AccessibilityNodeInfo button = mUiTestAutomationBridge
- .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
- assertNotNull(button);
- assertEquals(0, button.getChildCount());
-
- // bounds
- Rect bounds = new Rect();
- button.getBoundsInParent(bounds);
- assertEquals(0, bounds.left);
- assertEquals(0, bounds.top);
- assertEquals(160, bounds.right);
- assertEquals(100, bounds.bottom);
-
- // char sequence attributes
- assertEquals("com.android.frameworks.coretests", button.getPackageName());
- assertEquals("android.widget.Button", button.getClassName());
- assertEquals("Button5", button.getText());
- assertNull(button.getContentDescription());
-
- // boolean attributes
- assertTrue(button.isFocusable());
- assertTrue(button.isClickable());
- assertTrue(button.isEnabled());
- assertFalse(button.isFocused());
- assertTrue(button.isClickable());
- assertFalse(button.isPassword());
- assertFalse(button.isSelected());
- assertFalse(button.isCheckable());
- assertFalse(button.isChecked());
-
- // actions
- assertEquals(ACTION_FOCUS | ACTION_SELECT | ACTION_CLEAR_SELECTION,
- button.getActions());
- } finally {
- if (DEBUG) {
- final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
- Log.i(LOG_TAG, "testFindAccessibilityNodeInfoByViewId: "
- + elapsedTimeMillis + "ms");
- }
- }
- }
-
- @LargeTest
- public void testFindAccessibilityNodeInfoByViewText() throws Exception {
- final long startTimeMillis = SystemClock.uptimeMillis();
- try {
- // find a view by text
- List<AccessibilityNodeInfo> buttons = mUiTestAutomationBridge
- .findAccessibilityNodeInfosByTextInActiveWindow("butto");
- assertEquals(9, buttons.size());
- } finally {
- if (DEBUG) {
- final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
- Log.i(LOG_TAG, "testFindAccessibilityNodeInfoByViewText: "
- + elapsedTimeMillis + "ms");
- }
- }
- }
-
- @LargeTest
- public void testFindAccessibilityNodeInfoByViewTextContentDescription() throws Exception {
- final long startTimeMillis = SystemClock.uptimeMillis();
- try {
- // find a view by text
- List<AccessibilityNodeInfo> buttons = mUiTestAutomationBridge
- .findAccessibilityNodeInfosByTextInActiveWindow("contentDescription");
- assertEquals(1, buttons.size());
- } finally {
- if (DEBUG) {
- final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
- Log.i(LOG_TAG, "testFindAccessibilityNodeInfoByViewTextContentDescription: "
- + elapsedTimeMillis + "ms");
- }
- }
- }
-
- @LargeTest
- public void testTraverseAllViews() throws Exception {
- final long startTimeMillis = SystemClock.uptimeMillis();
- try {
- // make list of expected nodes
- List<String> classNameAndTextList = new ArrayList<String>();
- classNameAndTextList.add("android.widget.LinearLayout");
- classNameAndTextList.add("android.widget.LinearLayout");
- classNameAndTextList.add("android.widget.LinearLayout");
- classNameAndTextList.add("android.widget.LinearLayout");
- classNameAndTextList.add("android.widget.ButtonButton1");
- classNameAndTextList.add("android.widget.ButtonButton2");
- classNameAndTextList.add("android.widget.ButtonButton3");
- classNameAndTextList.add("android.widget.ButtonButton4");
- classNameAndTextList.add("android.widget.ButtonButton5");
- classNameAndTextList.add("android.widget.ButtonButton6");
- classNameAndTextList.add("android.widget.ButtonButton7");
- classNameAndTextList.add("android.widget.ButtonButton8");
- classNameAndTextList.add("android.widget.ButtonButton9");
-
- AccessibilityNodeInfo root = mUiTestAutomationBridge
- .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.root);
- assertNotNull("We must find the existing root.", root);
-
- Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
- fringe.add(root);
-
- // do a BFS traversal and check nodes
- while (!fringe.isEmpty()) {
- AccessibilityNodeInfo current = fringe.poll();
-
- CharSequence className = current.getClassName();
- CharSequence text = current.getText();
- String receivedClassNameAndText = className.toString()
- + ((text != null) ? text.toString() : "");
- String expectedClassNameAndText = classNameAndTextList.remove(0);
-
- assertEquals("Did not get the expected node info",
- expectedClassNameAndText, receivedClassNameAndText);
-
- final int childCount = current.getChildCount();
- for (int i = 0; i < childCount; i++) {
- AccessibilityNodeInfo child = current.getChild(i);
- fringe.add(child);
- }
- }
- } finally {
- if (DEBUG) {
- final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
- Log.i(LOG_TAG, "testTraverseAllViews: " + elapsedTimeMillis + "ms");
- }
- }
- }
-
- @LargeTest
- public void testPerformAccessibilityActionFocus() throws Exception {
- final long startTimeMillis = SystemClock.uptimeMillis();
- try {
- // find a view and make sure it is not focused
- AccessibilityNodeInfo button = mUiTestAutomationBridge
- .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
- assertFalse(button.isFocused());
-
- // focus the view
- assertTrue(button.performAction(ACTION_FOCUS));
-
- // find the view again and make sure it is focused
- button = mUiTestAutomationBridge
- .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
- assertTrue(button.isFocused());
- } finally {
- if (DEBUG) {
- final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
- Log.i(LOG_TAG, "testPerformAccessibilityActionFocus: " + elapsedTimeMillis + "ms");
- }
- }
- }
-
- @LargeTest
- public void testPerformAccessibilityActionClearFocus() throws Exception {
- final long startTimeMillis = SystemClock.uptimeMillis();
- try {
- // find a view and make sure it is not focused
- AccessibilityNodeInfo button = mUiTestAutomationBridge
- .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
- assertFalse(button.isFocused());
-
- // focus the view
- assertTrue(button.performAction(ACTION_FOCUS));
-
- // find the view again and make sure it is focused
- button = mUiTestAutomationBridge
- .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
- assertTrue(button.isFocused());
-
- // unfocus the view
- assertTrue(button.performAction(ACTION_CLEAR_FOCUS));
-
- // find the view again and make sure it is not focused
- button = mUiTestAutomationBridge
- .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
- assertFalse(button.isFocused());
- } finally {
- if (DEBUG) {
- final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
- Log.i(LOG_TAG, "testPerformAccessibilityActionClearFocus: "
- + elapsedTimeMillis + "ms");
- }
- }
- }
-
- @LargeTest
- public void testPerformAccessibilityActionSelect() throws Exception {
- final long startTimeMillis = SystemClock.uptimeMillis();
- try {
- // find a view and make sure it is not selected
- AccessibilityNodeInfo button = mUiTestAutomationBridge
- .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
- assertFalse(button.isSelected());
-
- // select the view
- assertTrue(button.performAction(ACTION_SELECT));
-
- // find the view again and make sure it is selected
- button = mUiTestAutomationBridge
- .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
- assertTrue(button.isSelected());
- } finally {
- if (DEBUG) {
- final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
- Log.i(LOG_TAG, "testPerformAccessibilityActionSelect: " + elapsedTimeMillis + "ms");
- }
- }
- }
-
- @LargeTest
- public void testPerformAccessibilityActionClearSelection() throws Exception {
- final long startTimeMillis = SystemClock.uptimeMillis();
- try {
- // find a view and make sure it is not selected
- AccessibilityNodeInfo button = mUiTestAutomationBridge
- .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
- assertFalse(button.isSelected());
-
- // select the view
- assertTrue(button.performAction(ACTION_SELECT));
-
- // find the view again and make sure it is selected
- button = mUiTestAutomationBridge
- .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
- assertTrue(button.isSelected());
-
- // unselect the view
- assertTrue(button.performAction(ACTION_CLEAR_SELECTION));
-
- // find the view again and make sure it is not selected
- button = mUiTestAutomationBridge
- .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
- assertFalse(button.isSelected());
- } finally {
- if (DEBUG) {
- final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
- Log.i(LOG_TAG, "testPerformAccessibilityActionClearSelection: "
- + elapsedTimeMillis + "ms");
- }
- }
- }
-
- @LargeTest
- public void testAccessibilityEventGetSource() throws Exception {
- final long startTimeMillis = SystemClock.uptimeMillis();
- try {
- // find a view and make sure it is not focused
- final AccessibilityNodeInfo button = mUiTestAutomationBridge
- .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
- assertFalse(button.isFocused());
-
- AccessibilityEvent event = mUiTestAutomationBridge
- .executeCommandAndWaitForAccessibilityEvent(new Runnable() {
- @Override
- public void run() {
- // focus the view
- assertTrue(button.performAction(ACTION_FOCUS));
- }
- },
- new Predicate<AccessibilityEvent>() {
- @Override
- public boolean apply(AccessibilityEvent event) {
- return (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED
- && event.getPackageName().equals(getActivity().getPackageName())
- && event.getText().get(0).equals(button.getText()));
- }
- },
- TIMEOUT_PROPAGATE_ACCESSIBILITY_EVENT_MILLIS);
-
- // check the last event
- assertNotNull(event);
-
- // check that last event source
- AccessibilityNodeInfo source = event.getSource();
- assertNotNull(source);
-
- // bounds
- Rect buttonBounds = new Rect();
- button.getBoundsInParent(buttonBounds);
- Rect sourceBounds = new Rect();
- source.getBoundsInParent(sourceBounds);
-
- assertEquals(buttonBounds.left, sourceBounds.left);
- assertEquals(buttonBounds.right, sourceBounds.right);
- assertEquals(buttonBounds.top, sourceBounds.top);
- assertEquals(buttonBounds.bottom, sourceBounds.bottom);
-
- // char sequence attributes
- assertEquals(button.getPackageName(), source.getPackageName());
- assertEquals(button.getClassName(), source.getClassName());
- assertEquals(button.getText(), source.getText());
- assertSame(button.getContentDescription(), source.getContentDescription());
-
- // boolean attributes
- assertSame(button.isFocusable(), source.isFocusable());
- assertSame(button.isClickable(), source.isClickable());
- assertSame(button.isEnabled(), source.isEnabled());
- assertNotSame(button.isFocused(), source.isFocused());
- assertSame(button.isLongClickable(), source.isLongClickable());
- assertSame(button.isPassword(), source.isPassword());
- assertSame(button.isSelected(), source.isSelected());
- assertSame(button.isCheckable(), source.isCheckable());
- assertSame(button.isChecked(), source.isChecked());
- } finally {
- if (DEBUG) {
- final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
- Log.i(LOG_TAG, "testAccessibilityEventGetSource: " + elapsedTimeMillis + "ms");
- }
- }
- }
-
- @LargeTest
- public void testObjectContract() throws Exception {
- final long startTimeMillis = SystemClock.uptimeMillis();
- try {
- // find a view and make sure it is not focused
- AccessibilityNodeInfo button = mUiTestAutomationBridge
- .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
- assertNotNull(button);
- AccessibilityNodeInfo parent = button.getParent();
- final int childCount = parent.getChildCount();
- for (int i = 0; i < childCount; i++) {
- AccessibilityNodeInfo child = parent.getChild(i);
- assertNotNull(child);
- if (child.equals(button)) {
- assertEquals("Equal objects must have same hasCode.", button.hashCode(),
- child.hashCode());
- return;
- }
- }
- fail("Parent's children do not have the info whose parent is the parent.");
- } finally {
- if (DEBUG) {
- final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
- Log.i(LOG_TAG, "testObjectContract: " + elapsedTimeMillis + "ms");
- }
- }
- }
-
- @LargeTest
- public void testGetRootAccessibilityNodeInfoInActiveWindow() throws Exception {
- final long startTimeMillis = SystemClock.uptimeMillis();
- try {
- // get the root via the designated API
- AccessibilityNodeInfo fetched = mUiTestAutomationBridge
- .getRootAccessibilityNodeInfoInActiveWindow();
- assertNotNull(fetched);
-
- // get the root via traversal
- AccessibilityNodeInfo expected = mUiTestAutomationBridge
- .findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.root);
- while (true) {
- AccessibilityNodeInfo parent = expected.getParent();
- if (parent == null) {
- break;
- }
- expected = parent;
- }
- assertNotNull(expected);
-
- assertEquals("The node with id \"root\" should be the root.", expected, fetched);
- } finally {
- if (DEBUG) {
- final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
- Log.i(LOG_TAG, "testGetRootAccessibilityNodeInfoInActiveWindow: "
- + elapsedTimeMillis + "ms");
- }
- }
- }
-}
diff --git a/core/tests/coretests/src/com/android/internal/util/FastXmlSerializerTest.java b/core/tests/coretests/src/com/android/internal/util/FastXmlSerializerTest.java
new file mode 100644
index 0000000..be7116d
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/FastXmlSerializerTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import junit.framework.TestCase;
+
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Tests for {@link FastXmlSerializer}
+ */
+public class FastXmlSerializerTest extends TestCase {
+ public void testEmptyText() throws Exception {
+ final ByteArrayOutputStream stream = new ByteArrayOutputStream();
+
+ final XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(stream, "utf-8");
+ out.startDocument(null, true);
+ out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+ out.startTag(null, "string");
+ out.attribute(null, "name", "meow");
+ out.text("");
+ out.endTag(null, "string");
+
+ out.endDocument();
+
+ assertEquals("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<string name=\"meow\"></string>", stream.toString());
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/util/StateMachineTest.java b/core/tests/coretests/src/com/android/internal/util/StateMachineTest.java
index 418bbd6..2a2c24e 100644
--- a/core/tests/coretests/src/com/android/internal/util/StateMachineTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/StateMachineTest.java
@@ -16,6 +16,9 @@
package com.android.internal.util;
+import java.util.Collection;
+import java.util.Iterator;
+
import android.os.Debug;
import android.os.HandlerThread;
import android.os.Looper;
@@ -58,11 +61,28 @@
}
}
+ private void dumpLogRecs(StateMachine sm) {
+ int size = sm.getLogRecSize();
+ tlog("size=" + size + " count=" + sm.getLogRecCount());
+ for (int i = 0; i < size; i++) {
+ LogRec lr = sm.getLogRec(i);
+ tlog(lr.toString());
+ }
+ }
+
+ private void dumpLogRecs(Collection<LogRec> clr) {
+ int size = clr.size();
+ tlog("size=" + size);
+ for (LogRec lr : clr) {
+ tlog(lr.toString());
+ }
+ }
+
/**
* Tests {@link StateMachine#quit()}.
*/
class StateMachineQuitTest extends StateMachine {
- Object mWaitUntilTestDone = new Object();
+ Collection<LogRec> mLogRecs;
StateMachineQuitTest(String name) {
super(name);
@@ -78,39 +98,32 @@
@Override
public void onQuitting() {
- Log.d(TAG, "onQuitting");
+ log("onQuitting");
addLogRec(ON_QUITTING);
+ mLogRecs = mThisSm.copyLogRecs();
synchronized (mThisSm) {
mThisSm.notifyAll();
}
-
- // Don't leave onQuitting before the test is done as everything is cleared
- // including the log records.
- synchronized (mWaitUntilTestDone) {
- try {
- mWaitUntilTestDone.wait();
- } catch(InterruptedException e) {
- }
- }
}
class S1 extends State {
+ @Override
public void exit() {
- Log.d(TAG, "S1.exit");
- addLogRec(EXIT, mS1);
+ log("S1.exit");
+ addLogRec(EXIT);
}
@Override
public boolean processMessage(Message message) {
switch(message.what) {
// Sleep and assume the other messages will be queued up.
case TEST_CMD_1: {
- Log.d(TAG, "TEST_CMD_1");
+ log("TEST_CMD_1");
sleep(500);
quit();
break;
}
default: {
- Log.d(TAG, "default what=" + message.what);
+ log("default what=" + message.what);
break;
}
}
@@ -128,7 +141,7 @@
StateMachineQuitTest smQuitTest = new StateMachineQuitTest("smQuitTest");
smQuitTest.start();
- if (smQuitTest.isDbg()) Log.d(TAG, "testStateMachineQuit E");
+ if (smQuitTest.isDbg()) tlog("testStateMachineQuit E");
synchronized (smQuitTest) {
@@ -141,38 +154,36 @@
// wait for the messages to be handled
smQuitTest.wait();
} catch (InterruptedException e) {
- Log.e(TAG, "testStateMachineQuit: exception while waiting " + e.getMessage());
+ tloge("testStateMachineQuit: exception while waiting " + e.getMessage());
}
}
- assertEquals(8, smQuitTest.getLogRecCount());
+ dumpLogRecs(smQuitTest.mLogRecs);
+ assertEquals(8, smQuitTest.mLogRecs.size());
LogRec lr;
-
- for (int i = 0; i < 6; i++) {
- lr = smQuitTest.getLogRec(i);
- assertEquals(i+1, lr.getWhat());
+ Iterator<LogRec> itr = smQuitTest.mLogRecs.iterator();
+ for (int i = 1; i <= 6; i++) {
+ lr = itr.next();
+ assertEquals(i, lr.getWhat());
assertEquals(smQuitTest.mS1, lr.getState());
assertEquals(smQuitTest.mS1, lr.getOriginalState());
}
- lr = smQuitTest.getLogRec(6);
+ lr = itr.next();
assertEquals(EXIT, lr.getInfo());
assertEquals(smQuitTest.mS1, lr.getState());
- lr = smQuitTest.getLogRec(7);
+ lr = itr.next();
assertEquals(ON_QUITTING, lr.getInfo());
- synchronized (smQuitTest.mWaitUntilTestDone) {
- smQuitTest.mWaitUntilTestDone.notifyAll();
- }
- if (smQuitTest.isDbg()) Log.d(TAG, "testStateMachineQuit X");
+ if (smQuitTest.isDbg()) tlog("testStateMachineQuit X");
}
/**
* Tests {@link StateMachine#quitNow()}
*/
class StateMachineQuitNowTest extends StateMachine {
- Object mWaitUntilTestDone = new Object();
+ public Collection<LogRec> mLogRecs = null;
StateMachineQuitNowTest(String name) {
super(name);
@@ -188,39 +199,34 @@
@Override
public void onQuitting() {
- Log.d(TAG, "onQuitting");
+ log("onQuitting");
addLogRec(ON_QUITTING);
+ // Get a copy of the log records since we're quitting and they will disappear
+ mLogRecs = mThisSm.copyLogRecs();
+
synchronized (mThisSm) {
mThisSm.notifyAll();
}
-
- // Don't leave onQuitting before the test is done as everything is cleared
- // including the log records.
- synchronized (mWaitUntilTestDone) {
- try {
- mWaitUntilTestDone.wait();
- } catch(InterruptedException e) {
- }
- }
}
class S1 extends State {
+ @Override
public void exit() {
- Log.d(TAG, "S1.exit");
- addLogRec(EXIT, mS1);
+ log("S1.exit");
+ addLogRec(EXIT);
}
@Override
public boolean processMessage(Message message) {
switch(message.what) {
// Sleep and assume the other messages will be queued up.
case TEST_CMD_1: {
- Log.d(TAG, "TEST_CMD_1");
+ log("TEST_CMD_1");
sleep(500);
quitNow();
break;
}
default: {
- Log.d(TAG, "default what=" + message.what);
+ log("default what=" + message.what);
break;
}
}
@@ -238,11 +244,11 @@
StateMachineQuitNowTest smQuitNowTest = new StateMachineQuitNowTest("smQuitNowTest");
smQuitNowTest.start();
- if (smQuitNowTest.isDbg()) Log.d(TAG, "testStateMachineQuitNow E");
+ if (smQuitNowTest.isDbg()) tlog("testStateMachineQuitNow E");
synchronized (smQuitNowTest) {
- // Send 6 messages but we'll QuitNow on the first so even though
+ // Send 6 message we'll QuitNow on the first even though
// we send 6 only one will be processed.
for (int i = 1; i <= 6; i++) {
smQuitNowTest.sendMessage(smQuitNowTest.obtainMessage(i));
@@ -252,31 +258,27 @@
// wait for the messages to be handled
smQuitNowTest.wait();
} catch (InterruptedException e) {
- Log.e(TAG, "testStateMachineQuitNow: exception while waiting " + e.getMessage());
+ tloge("testStateMachineQuitNow: exception while waiting " + e.getMessage());
}
}
- // Only three records because we executed quitNow.
- assertEquals(3, smQuitNowTest.getLogRecCount());
+ tlog("testStateMachineQuiteNow: logRecs=" + smQuitNowTest.mLogRecs);
+ assertEquals(3, smQuitNowTest.mLogRecs.size());
- LogRec lr;
-
- lr = smQuitNowTest.getLogRec(0);
+ Iterator<LogRec> itr = smQuitNowTest.mLogRecs.iterator();
+ LogRec lr = itr.next();
assertEquals(1, lr.getWhat());
assertEquals(smQuitNowTest.mS1, lr.getState());
assertEquals(smQuitNowTest.mS1, lr.getOriginalState());
- lr = smQuitNowTest.getLogRec(1);
+ lr = itr.next();
assertEquals(EXIT, lr.getInfo());
assertEquals(smQuitNowTest.mS1, lr.getState());
- lr = smQuitNowTest.getLogRec(2);
+ lr = itr.next();
assertEquals(ON_QUITTING, lr.getInfo());
- synchronized (smQuitNowTest.mWaitUntilTestDone) {
- smQuitNowTest.mWaitUntilTestDone.notifyAll();
- }
- if (smQuitNowTest.isDbg()) Log.d(TAG, "testStateMachineQuitNow X");
+ if (smQuitNowTest.isDbg()) tlog("testStateMachineQuitNow X");
}
/**
@@ -303,39 +305,39 @@
@Override
public void enter() {
// Test transitions in enter on the initial state work
- addLogRec(ENTER, mS1);
+ addLogRec(ENTER);
transitionTo(mS2);
- Log.d(TAG, "S1.enter");
+ log("S1.enter");
}
@Override
public void exit() {
- // Test that message is HSM_INIT_CMD
- addLogRec(EXIT, mS1);
- Log.d(TAG, "S1.exit");
+ addLogRec(EXIT);
+ log("S1.exit");
}
}
class S2 extends State {
@Override
public void enter() {
- addLogRec(ENTER, mS2);
- Log.d(TAG, "S2.enter");
+ addLogRec(ENTER);
+ log("S2.enter");
}
@Override
public void exit() {
- addLogRec(EXIT, mS2);
- assertEquals(TEST_CMD_1, getCurrentMessage().what);
-
// Test transition in exit work
transitionTo(mS4);
- Log.d(TAG, "S2.exit");
+
+ assertEquals(TEST_CMD_1, getCurrentMessage().what);
+ addLogRec(EXIT);
+
+ log("S2.exit");
}
@Override
public boolean processMessage(Message message) {
// Start a transition to S3 but it will be
// changed to a transition to S4 in exit
transitionTo(mS3);
- Log.d(TAG, "S2.processMessage");
+ log("S2.processMessage");
return HANDLED;
}
}
@@ -343,28 +345,28 @@
class S3 extends State {
@Override
public void enter() {
- addLogRec(ENTER, mS3);
- Log.d(TAG, "S3.enter");
+ addLogRec(ENTER);
+ log("S3.enter");
}
@Override
public void exit() {
- addLogRec(EXIT, mS3);
- Log.d(TAG, "S3.exit");
+ addLogRec(EXIT);
+ log("S3.exit");
}
}
class S4 extends State {
@Override
public void enter() {
- addLogRec(ENTER, mS4);
+ addLogRec(ENTER);
// Test that we can do halting in an enter/exit
transitionToHaltingState();
- Log.d(TAG, "S4.enter");
+ log("S4.enter");
}
@Override
public void exit() {
- addLogRec(EXIT, mS4);
- Log.d(TAG, "S4.exit");
+ addLogRec(EXIT);
+ log("S4.exit");
}
}
@@ -390,7 +392,7 @@
new StateMachineEnterExitTransitionToTest("smEnterExitTranstionToTest");
smEnterExitTranstionToTest.start();
if (smEnterExitTranstionToTest.isDbg()) {
- Log.d(TAG, "testStateMachineEnterExitTransitionToTest E");
+ tlog("testStateMachineEnterExitTransitionToTest E");
}
synchronized (smEnterExitTranstionToTest) {
@@ -400,13 +402,14 @@
// wait for the messages to be handled
smEnterExitTranstionToTest.wait();
} catch (InterruptedException e) {
- Log.e(TAG, "testStateMachineEnterExitTransitionToTest: exception while waiting "
+ tloge("testStateMachineEnterExitTransitionToTest: exception while waiting "
+ e.getMessage());
}
}
- assertEquals(smEnterExitTranstionToTest.getLogRecCount(), 9);
+ dumpLogRecs(smEnterExitTranstionToTest);
+ assertEquals(9, smEnterExitTranstionToTest.getLogRecCount());
LogRec lr;
lr = smEnterExitTranstionToTest.getLogRec(0);
@@ -425,29 +428,44 @@
assertEquals(TEST_CMD_1, lr.getWhat());
assertEquals(smEnterExitTranstionToTest.mS2, lr.getState());
assertEquals(smEnterExitTranstionToTest.mS2, lr.getOriginalState());
+ assertEquals(smEnterExitTranstionToTest.mS3, lr.getDestState());
lr = smEnterExitTranstionToTest.getLogRec(4);
- assertEquals(EXIT, lr.getInfo());
+ assertEquals(TEST_CMD_1, lr.getWhat());
assertEquals(smEnterExitTranstionToTest.mS2, lr.getState());
+ assertEquals(smEnterExitTranstionToTest.mS2, lr.getOriginalState());
+ assertEquals(smEnterExitTranstionToTest.mS4, lr.getDestState());
+ assertEquals(EXIT, lr.getInfo());
lr = smEnterExitTranstionToTest.getLogRec(5);
+ assertEquals(TEST_CMD_1, lr.getWhat());
assertEquals(ENTER, lr.getInfo());
assertEquals(smEnterExitTranstionToTest.mS3, lr.getState());
+ assertEquals(smEnterExitTranstionToTest.mS3, lr.getOriginalState());
+ assertEquals(smEnterExitTranstionToTest.mS4, lr.getDestState());
lr = smEnterExitTranstionToTest.getLogRec(6);
+ assertEquals(TEST_CMD_1, lr.getWhat());
assertEquals(EXIT, lr.getInfo());
assertEquals(smEnterExitTranstionToTest.mS3, lr.getState());
+ assertEquals(smEnterExitTranstionToTest.mS3, lr.getOriginalState());
+ assertEquals(smEnterExitTranstionToTest.mS4, lr.getDestState());
lr = smEnterExitTranstionToTest.getLogRec(7);
+ assertEquals(TEST_CMD_1, lr.getWhat());
assertEquals(ENTER, lr.getInfo());
assertEquals(smEnterExitTranstionToTest.mS4, lr.getState());
+ assertEquals(smEnterExitTranstionToTest.mS4, lr.getOriginalState());
+ assertEquals(smEnterExitTranstionToTest.mS4, lr.getDestState());
lr = smEnterExitTranstionToTest.getLogRec(8);
+ assertEquals(TEST_CMD_1, lr.getWhat());
assertEquals(EXIT, lr.getInfo());
assertEquals(smEnterExitTranstionToTest.mS4, lr.getState());
+ assertEquals(smEnterExitTranstionToTest.mS4, lr.getOriginalState());
if (smEnterExitTranstionToTest.isDbg()) {
- Log.d(TAG, "testStateMachineEnterExitTransitionToTest X");
+ tlog("testStateMachineEnterExitTransitionToTest X");
}
}
@@ -495,7 +513,7 @@
StateMachine0 sm0 = new StateMachine0("sm0");
sm0.start();
- if (sm0.isDbg()) Log.d(TAG, "testStateMachine0 E");
+ if (sm0.isDbg()) tlog("testStateMachine0 E");
synchronized (sm0) {
// Send 6 messages
@@ -507,13 +525,15 @@
// wait for the messages to be handled
sm0.wait();
} catch (InterruptedException e) {
- Log.e(TAG, "testStateMachine0: exception while waiting " + e.getMessage());
+ tloge("testStateMachine0: exception while waiting " + e.getMessage());
}
}
assertEquals(6, sm0.getLogRecCount());
assertEquals(3, sm0.getLogRecSize());
+ dumpLogRecs(sm0);
+
LogRec lr;
lr = sm0.getLogRec(0);
assertEquals(TEST_CMD_4, lr.getWhat());
@@ -530,7 +550,7 @@
assertEquals(sm0.mS1, lr.getState());
assertEquals(sm0.mS1, lr.getOriginalState());
- if (sm0.isDbg()) Log.d(TAG, "testStateMachine0 X");
+ if (sm0.isDbg()) tlog("testStateMachine0 X");
}
/**
@@ -550,7 +570,7 @@
// Set the initial state
setInitialState(mS1);
- if (DBG) Log.d(TAG, "StateMachine1: ctor X");
+ if (DBG) log("StateMachine1: ctor X");
}
class S1 extends State {
@@ -595,7 +615,7 @@
public void testStateMachine1() throws Exception {
StateMachine1 sm1 = new StateMachine1("sm1");
sm1.start();
- if (sm1.isDbg()) Log.d(TAG, "testStateMachine1 E");
+ if (sm1.isDbg()) tlog("testStateMachine1 E");
synchronized (sm1) {
// Send two messages
@@ -606,7 +626,7 @@
// wait for the messages to be handled
sm1.wait();
} catch (InterruptedException e) {
- Log.e(TAG, "testStateMachine1: exception while waiting " + e.getMessage());
+ tloge("testStateMachine1: exception while waiting " + e.getMessage());
}
}
@@ -629,7 +649,7 @@
assertEquals(2, sm1.mEnterCount);
assertEquals(2, sm1.mExitCount);
- if (sm1.isDbg()) Log.d(TAG, "testStateMachine1 X");
+ if (sm1.isDbg()) tlog("testStateMachine1 X");
}
/**
@@ -651,7 +671,7 @@
// Set the initial state
setInitialState(mS1);
- if (DBG) Log.d(TAG, "StateMachine2: ctor X");
+ if (DBG) log("StateMachine2: ctor X");
}
class S1 extends State {
@@ -702,7 +722,7 @@
public void testStateMachine2() throws Exception {
StateMachine2 sm2 = new StateMachine2("sm2");
sm2.start();
- if (sm2.isDbg()) Log.d(TAG, "testStateMachine2 E");
+ if (sm2.isDbg()) tlog("testStateMachine2 E");
synchronized (sm2) {
// Send two messages
@@ -713,7 +733,7 @@
// wait for the messages to be handled
sm2.wait();
} catch (InterruptedException e) {
- Log.e(TAG, "testStateMachine2: exception while waiting " + e.getMessage());
+ tloge("testStateMachine2: exception while waiting " + e.getMessage());
}
}
@@ -739,7 +759,7 @@
assertTrue(sm2.mDidEnter);
assertTrue(sm2.mDidExit);
- if (sm2.isDbg()) Log.d(TAG, "testStateMachine2 X");
+ if (sm2.isDbg()) tlog("testStateMachine2 X");
}
/**
@@ -760,7 +780,7 @@
// Set the initial state will be the child
setInitialState(mChildState);
- if (DBG) Log.d(TAG, "StateMachine3: ctor X");
+ if (DBG) log("StateMachine3: ctor X");
}
class ParentState extends State {
@@ -796,7 +816,7 @@
public void testStateMachine3() throws Exception {
StateMachine3 sm3 = new StateMachine3("sm3");
sm3.start();
- if (sm3.isDbg()) Log.d(TAG, "testStateMachine3 E");
+ if (sm3.isDbg()) tlog("testStateMachine3 E");
synchronized (sm3) {
// Send two messages
@@ -807,7 +827,7 @@
// wait for the messages to be handled
sm3.wait();
} catch (InterruptedException e) {
- Log.e(TAG, "testStateMachine3: exception while waiting " + e.getMessage());
+ tloge("testStateMachine3: exception while waiting " + e.getMessage());
}
}
@@ -824,7 +844,7 @@
assertEquals(sm3.mParentState, lr.getState());
assertEquals(sm3.mChildState, lr.getOriginalState());
- if (sm3.isDbg()) Log.d(TAG, "testStateMachine3 X");
+ if (sm3.isDbg()) tlog("testStateMachine3 X");
}
/**
@@ -847,7 +867,7 @@
// Set the initial state will be child 1
setInitialState(mChildState1);
- if (DBG) Log.d(TAG, "StateMachine4: ctor X");
+ if (DBG) log("StateMachine4: ctor X");
}
class ParentState extends State {
@@ -892,7 +912,7 @@
public void testStateMachine4() throws Exception {
StateMachine4 sm4 = new StateMachine4("sm4");
sm4.start();
- if (sm4.isDbg()) Log.d(TAG, "testStateMachine4 E");
+ if (sm4.isDbg()) tlog("testStateMachine4 E");
synchronized (sm4) {
// Send two messages
@@ -903,7 +923,7 @@
// wait for the messages to be handled
sm4.wait();
} catch (InterruptedException e) {
- Log.e(TAG, "testStateMachine4: exception while waiting " + e.getMessage());
+ tloge("testStateMachine4: exception while waiting " + e.getMessage());
}
}
@@ -921,7 +941,7 @@
assertEquals(sm4.mParentState, lr.getState());
assertEquals(sm4.mChildState2, lr.getOriginalState());
- if (sm4.isDbg()) Log.d(TAG, "testStateMachine4 X");
+ if (sm4.isDbg()) tlog("testStateMachine4 X");
}
/**
@@ -947,7 +967,7 @@
// Set the initial state will be the child
setInitialState(mChildState1);
- if (DBG) Log.d(TAG, "StateMachine5: ctor X");
+ if (DBG) log("StateMachine5: ctor X");
}
class ParentState1 extends State {
@@ -1187,7 +1207,7 @@
public void testStateMachine5() throws Exception {
StateMachine5 sm5 = new StateMachine5("sm5");
sm5.start();
- if (sm5.isDbg()) Log.d(TAG, "testStateMachine5 E");
+ if (sm5.isDbg()) tlog("testStateMachine5 E");
synchronized (sm5) {
// Send 6 messages
@@ -1202,7 +1222,7 @@
// wait for the messages to be handled
sm5.wait();
} catch (InterruptedException e) {
- Log.e(TAG, "testStateMachine5: exception while waiting " + e.getMessage());
+ tloge("testStateMachine5: exception while waiting " + e.getMessage());
}
}
@@ -1255,7 +1275,7 @@
assertEquals(sm5.mParentState2, lr.getState());
assertEquals(sm5.mParentState2, lr.getOriginalState());
- if (sm5.isDbg()) Log.d(TAG, "testStateMachine5 X");
+ if (sm5.isDbg()) tlog("testStateMachine5 X");
}
/**
@@ -1274,7 +1294,7 @@
// Set the initial state
setInitialState(mS1);
- if (DBG) Log.d(TAG, "StateMachine6: ctor X");
+ if (DBG) log("StateMachine6: ctor X");
}
class S1 extends State {
@@ -1315,7 +1335,7 @@
StateMachine6 sm6 = new StateMachine6("sm6");
sm6.start();
- if (sm6.isDbg()) Log.d(TAG, "testStateMachine6 E");
+ if (sm6.isDbg()) tlog("testStateMachine6 E");
synchronized (sm6) {
// Send a message
@@ -1325,7 +1345,7 @@
// wait for the messages to be handled
sm6.wait();
} catch (InterruptedException e) {
- Log.e(TAG, "testStateMachine6: exception while waiting " + e.getMessage());
+ tloge("testStateMachine6: exception while waiting " + e.getMessage());
}
}
@@ -1336,11 +1356,11 @@
*/
long arrivalTimeDiff = sm6.mArrivalTimeMsg2 - sm6.mArrivalTimeMsg1;
long expectedDelay = DELAY_TIME - DELAY_FUDGE;
- if (sm6.isDbg()) Log.d(TAG, "testStateMachine6: expect " + arrivalTimeDiff
+ if (sm6.isDbg()) tlog("testStateMachine6: expect " + arrivalTimeDiff
+ " >= " + expectedDelay);
assertTrue(arrivalTimeDiff >= expectedDelay);
- if (sm6.isDbg()) Log.d(TAG, "testStateMachine6 X");
+ if (sm6.isDbg()) tlog("testStateMachine6 X");
}
/**
@@ -1361,7 +1381,7 @@
// Set the initial state
setInitialState(mS1);
- if (DBG) Log.d(TAG, "StateMachine7: ctor X");
+ if (DBG) log("StateMachine7: ctor X");
}
class S1 extends State {
@@ -1421,7 +1441,7 @@
StateMachine7 sm7 = new StateMachine7("sm7");
sm7.start();
- if (sm7.isDbg()) Log.d(TAG, "testStateMachine7 E");
+ if (sm7.isDbg()) tlog("testStateMachine7 E");
synchronized (sm7) {
// Send a message
@@ -1431,7 +1451,7 @@
// wait for the messages to be handled
sm7.wait();
} catch (InterruptedException e) {
- Log.e(TAG, "testStateMachine7: exception while waiting " + e.getMessage());
+ tloge("testStateMachine7: exception while waiting " + e.getMessage());
}
}
@@ -1442,11 +1462,11 @@
*/
long arrivalTimeDiff = sm7.mArrivalTimeMsg3 - sm7.mArrivalTimeMsg2;
long expectedDelay = sm7.SM7_DELAY_TIME - SM7_DELAY_FUDGE;
- if (sm7.isDbg()) Log.d(TAG, "testStateMachine7: expect " + arrivalTimeDiff
+ if (sm7.isDbg()) tlog("testStateMachine7: expect " + arrivalTimeDiff
+ " >= " + expectedDelay);
assertTrue(arrivalTimeDiff >= expectedDelay);
- if (sm7.isDbg()) Log.d(TAG, "testStateMachine7 X");
+ if (sm7.isDbg()) tlog("testStateMachine7 X");
}
/**
@@ -1494,9 +1514,9 @@
@SmallTest
public void testStateMachineUnhandledMessage() throws Exception {
- StateMachineUnhandledMessage sm = new StateMachineUnhandledMessage("sm");
+ StateMachineUnhandledMessage sm = new StateMachineUnhandledMessage("smUnhandledMessage");
sm.start();
- if (sm.isDbg()) Log.d(TAG, "testStateMachineUnhandledMessage E");
+ if (sm.isDbg()) tlog("testStateMachineUnhandledMessage E");
synchronized (sm) {
// Send 2 messages
@@ -1508,15 +1528,15 @@
// wait for the messages to be handled
sm.wait();
} catch (InterruptedException e) {
- Log.e(TAG, "testStateMachineUnhandledMessage: exception while waiting "
+ tloge("testStateMachineUnhandledMessage: exception while waiting "
+ e.getMessage());
}
}
- assertEquals(sm.getLogRecCount(), 2);
+ assertEquals(2, sm.getLogRecSize());
assertEquals(2, sm.mUnhandledMessageCount);
- if (sm.isDbg()) Log.d(TAG, "testStateMachineUnhandledMessage X");
+ if (sm.isDbg()) tlog("testStateMachineUnhandledMessage X");
}
/**
@@ -1569,7 +1589,7 @@
@MediumTest
public void testStateMachineSharedThread() throws Exception {
- if (DBG) Log.d(TAG, "testStateMachineSharedThread E");
+ if (DBG) tlog("testStateMachineSharedThread E");
// Create and start the handler thread
HandlerThread smThread = new HandlerThread("testStateMachineSharedThread");
@@ -1578,7 +1598,8 @@
// Create the state machines
StateMachineSharedThread sms[] = new StateMachineSharedThread[10];
for (int i = 0; i < sms.length; i++) {
- sms[i] = new StateMachineSharedThread("sm", smThread.getLooper(), sms.length);
+ sms[i] = new StateMachineSharedThread("smSharedThread",
+ smThread.getLooper(), sms.length);
sms[i].start();
}
@@ -1594,14 +1615,14 @@
try {
waitObject.wait();
} catch (InterruptedException e) {
- Log.e(TAG, "testStateMachineSharedThread: exception while waiting "
+ tloge("testStateMachineSharedThread: exception while waiting "
+ e.getMessage());
}
}
for (StateMachineSharedThread sm : sms) {
- assertEquals(sm.getLogRecCount(), 4);
- for (int i = 0; i < sm.getLogRecCount(); i++) {
+ assertEquals(4, sm.getLogRecCount());
+ for (int i = 0; i < sm.getLogRecSize(); i++) {
LogRec lr = sm.getLogRec(i);
assertEquals(i+1, lr.getWhat());
assertEquals(sm.mS1, lr.getState());
@@ -1609,12 +1630,168 @@
}
}
- if (DBG) Log.d(TAG, "testStateMachineSharedThread X");
+ if (DBG) tlog("testStateMachineSharedThread X");
+ }
+
+ static class Hsm1 extends StateMachine {
+ private static final String HSM1_TAG = "hsm1";
+
+ public static final int CMD_1 = 1;
+ public static final int CMD_2 = 2;
+ public static final int CMD_3 = 3;
+ public static final int CMD_4 = 4;
+ public static final int CMD_5 = 5;
+
+ public static Hsm1 makeHsm1() {
+ Log.d(HSM1_TAG, "makeHsm1 E");
+ Hsm1 sm = new Hsm1(HSM1_TAG);
+ sm.start();
+ Log.d(HSM1_TAG, "makeHsm1 X");
+ return sm;
+ }
+
+ Hsm1(String name) {
+ super(name);
+ log("ctor E");
+
+ // Add states, use indentation to show hierarchy
+ addState(mP1);
+ addState(mS1, mP1);
+ addState(mS2, mP1);
+ addState(mP2);
+
+ // Set the initial state
+ setInitialState(mS1);
+ log("ctor X");
+ }
+
+ class P1 extends State {
+ @Override
+ public void enter() {
+ log("P1.enter");
+ }
+ @Override
+ public void exit() {
+ log("P1.exit");
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ boolean retVal;
+ log("P1.processMessage what=" + message.what);
+ switch(message.what) {
+ case CMD_2:
+ // CMD_2 will arrive in mS2 before CMD_3
+ sendMessage(CMD_3);
+ deferMessage(message);
+ transitionTo(mS2);
+ retVal = true;
+ break;
+ default:
+ // Any message we don't understand in this state invokes unhandledMessage
+ retVal = false;
+ break;
+ }
+ return retVal;
+ }
+ }
+
+ class S1 extends State {
+ @Override
+ public void enter() {
+ log("S1.enter");
+ }
+ @Override
+ public void exit() {
+ log("S1.exit");
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ log("S1.processMessage what=" + message.what);
+ if (message.what == CMD_1) {
+ // Transition to ourself to show that enter/exit is called
+ transitionTo(mS1);
+ return HANDLED;
+ } else {
+ // Let parent process all other messages
+ return NOT_HANDLED;
+ }
+ }
+ }
+
+ class S2 extends State {
+ @Override
+ public void enter() {
+ log("S2.enter");
+ }
+ @Override
+ public void exit() {
+ log("S2.exit");
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ boolean retVal;
+ log("S2.processMessage what=" + message.what);
+ switch(message.what) {
+ case(CMD_2):
+ sendMessage(CMD_4);
+ retVal = true;
+ break;
+ case(CMD_3):
+ deferMessage(message);
+ transitionTo(mP2);
+ retVal = true;
+ break;
+ default:
+ retVal = false;
+ break;
+ }
+ return retVal;
+ }
+ }
+
+ class P2 extends State {
+ @Override
+ public void enter() {
+ log("P2.enter");
+ sendMessage(CMD_5);
+ }
+ @Override
+ public void exit() {
+ log("P2.exit");
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ log("P2.processMessage what=" + message.what);
+ switch(message.what) {
+ case(CMD_3):
+ break;
+ case(CMD_4):
+ break;
+ case(CMD_5):
+ transitionToHaltingState();
+ break;
+ }
+ return HANDLED;
+ }
+ }
+
+ @Override
+ protected void onHalting() {
+ log("halting");
+ synchronized (this) {
+ this.notifyAll();
+ }
+ }
+
+ P1 mP1 = new P1();
+ S1 mS1 = new S1();
+ S2 mS2 = new S2();
+ P2 mP2 = new P2();
}
@MediumTest
public void testHsm1() throws Exception {
- if (DBG) Log.d(TAG, "testHsm1 E");
+ if (DBG) tlog("testHsm1 E");
Hsm1 sm = Hsm1.makeHsm1();
@@ -1627,11 +1804,14 @@
try {
sm.wait();
} catch (InterruptedException e) {
- Log.e(TAG, "testHsm1: exception while waiting " + e.getMessage());
+ tloge("testHsm1: exception while waiting " + e.getMessage());
}
}
+ dumpLogRecs(sm);
+
assertEquals(7, sm.getLogRecCount());
+
LogRec lr = sm.getLogRec(0);
assertEquals(Hsm1.CMD_1, lr.getWhat());
assertEquals(sm.mS1, lr.getState());
@@ -1667,162 +1847,14 @@
assertEquals(sm.mP2, lr.getState());
assertEquals(sm.mP2, lr.getOriginalState());
- if (DBG) Log.d(TAG, "testStateMachineSharedThread X");
- }
-}
-
-class Hsm1 extends StateMachine {
- private static final String TAG = "hsm1";
-
- public static final int CMD_1 = 1;
- public static final int CMD_2 = 2;
- public static final int CMD_3 = 3;
- public static final int CMD_4 = 4;
- public static final int CMD_5 = 5;
-
- public static Hsm1 makeHsm1() {
- Log.d(TAG, "makeHsm1 E");
- Hsm1 sm = new Hsm1("hsm1");
- sm.start();
- Log.d(TAG, "makeHsm1 X");
- return sm;
+ if (DBG) tlog("testStateMachineSharedThread X");
}
- Hsm1(String name) {
- super(name);
- Log.d(TAG, "ctor E");
-
- // Add states, use indentation to show hierarchy
- addState(mP1);
- addState(mS1, mP1);
- addState(mS2, mP1);
- addState(mP2);
-
- // Set the initial state
- setInitialState(mS1);
- Log.d(TAG, "ctor X");
+ private void tlog(String s) {
+ Log.d(TAG, s);
}
- class P1 extends State {
- @Override
- public void enter() {
- Log.d(TAG, "P1.enter");
- }
- @Override
- public void exit() {
- Log.d(TAG, "P1.exit");
- }
- @Override
- public boolean processMessage(Message message) {
- boolean retVal;
- Log.d(TAG, "P1.processMessage what=" + message.what);
- switch(message.what) {
- case CMD_2:
- // CMD_2 will arrive in mS2 before CMD_3
- sendMessage(CMD_3);
- deferMessage(message);
- transitionTo(mS2);
- retVal = true;
- break;
- default:
- // Any message we don't understand in this state invokes unhandledMessage
- retVal = false;
- break;
- }
- return retVal;
- }
+ private void tloge(String s) {
+ Log.e(TAG, s);
}
-
- class S1 extends State {
- @Override
- public void enter() {
- Log.d(TAG, "S1.enter");
- }
- @Override
- public void exit() {
- Log.d(TAG, "S1.exit");
- }
- @Override
- public boolean processMessage(Message message) {
- Log.d(TAG, "S1.processMessage what=" + message.what);
- if (message.what == CMD_1) {
- // Transition to ourself to show that enter/exit is called
- transitionTo(mS1);
- return HANDLED;
- } else {
- // Let parent process all other messages
- return NOT_HANDLED;
- }
- }
- }
-
- class S2 extends State {
- @Override
- public void enter() {
- Log.d(TAG, "S2.enter");
- }
- @Override
- public void exit() {
- Log.d(TAG, "S2.exit");
- }
- @Override
- public boolean processMessage(Message message) {
- boolean retVal;
- Log.d(TAG, "S2.processMessage what=" + message.what);
- switch(message.what) {
- case(CMD_2):
- sendMessage(CMD_4);
- retVal = true;
- break;
- case(CMD_3):
- deferMessage(message);
- transitionTo(mP2);
- retVal = true;
- break;
- default:
- retVal = false;
- break;
- }
- return retVal;
- }
- }
-
- class P2 extends State {
- @Override
- public void enter() {
- Log.d(TAG, "P2.enter");
- sendMessage(CMD_5);
- }
- @Override
- public void exit() {
- Log.d(TAG, "P2.exit");
- }
- @Override
- public boolean processMessage(Message message) {
- Log.d(TAG, "P2.processMessage what=" + message.what);
- switch(message.what) {
- case(CMD_3):
- break;
- case(CMD_4):
- break;
- case(CMD_5):
- transitionToHaltingState();
- break;
- }
- return HANDLED;
- }
- }
-
- @Override
- protected void onHalting() {
- Log.d(TAG, "halting");
- synchronized (this) {
- this.notifyAll();
- }
- }
-
- P1 mP1 = new P1();
- S1 mS1 = new S1();
- S2 mS2 = new S2();
- P2 mP2 = new P2();
}
diff --git a/core/tests/inputmethodtests/Android.mk b/core/tests/inputmethodtests/Android.mk
new file mode 100644
index 0000000..4631e65
--- /dev/null
+++ b/core/tests/inputmethodtests/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+# Include all test java files.
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src)
+
+LOCAL_DX_FLAGS := --core-library
+LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-common frameworks-core-util-lib
+LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_PACKAGE_NAME := FrameworksCoreInputMethodTests
+
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/inputmethodtests/AndroidManifest.xml b/core/tests/inputmethodtests/AndroidManifest.xml
new file mode 100644
index 0000000..7f0b1aa
--- /dev/null
+++ b/core/tests/inputmethodtests/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ android:installLocation="internalOnly"
+ package="com.android.frameworks.coretests.inputmethod"
+ android:sharedUserId="android.uid.system">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="com.android.frameworks.coretests.inputmethod"
+ android:label="Frameworks InputMethod Core Tests" />
+
+</manifest>
diff --git a/core/tests/inputmethodtests/run_core_inputmethod_test.sh b/core/tests/inputmethodtests/run_core_inputmethod_test.sh
new file mode 100755
index 0000000..5e123ec
--- /dev/null
+++ b/core/tests/inputmethodtests/run_core_inputmethod_test.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --rebuild ) echo Rebuild && rebuild=true;;
+ * ) com_opts+=($1);;
+ esac
+ shift
+done
+
+if [[ -z $ANDROID_PRODUCT_OUT && $rebuilld == true ]]; then
+ echo You must lunch before running this test.
+ exit 0
+fi
+
+if [[ $rebuild == true ]]; then
+ make -j4 FrameworksCoreInputMethodTests
+ TESTAPP=${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreInputMethodTests.apk
+ COMMAND="adb install -r $TESTAPP"
+ echo $COMMAND
+ $COMMAND
+fi
+
+adb shell am instrument -w -e class android.os.InputMethodTest com.android.frameworks.coretests.inputmethod/android.test.InstrumentationTestRunner
diff --git a/core/tests/inputmethodtests/src/android/os/InputMethodTest.java b/core/tests/inputmethodtests/src/android/os/InputMethodTest.java
new file mode 100644
index 0000000..0a2b50c
--- /dev/null
+++ b/core/tests/inputmethodtests/src/android/os/InputMethodTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import com.android.internal.inputmethod.InputMethodUtils;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class InputMethodTest extends InstrumentationTestCase {
+ private static final boolean IS_AUX = true;
+ private static final boolean IS_DEFAULT = true;
+ private static final boolean IS_AUTO = true;
+
+ @SmallTest
+ public void testDefaultEnabledImesWithDefaultVoiceIme() throws Exception {
+ final Context context = getInstrumentation().getTargetContext();
+ final ArrayList<InputMethodInfo> imis = new ArrayList<InputMethodInfo>();
+ imis.add(createDefaultAutoDummyVoiceIme());
+ imis.add(createNonDefaultAutoDummyVoiceIme0());
+ imis.add(createNonDefaultAutoDummyVoiceIme1());
+ imis.add(createNonDefaultDummyVoiceIme2());
+ imis.add(createDefaultDummyEnUSKeyboardIme());
+ imis.add(createNonDefaultDummyJaJPKeyboardIme());
+ final ArrayList<InputMethodInfo> enabledImis = InputMethodUtils.getDefaultEnabledImes(
+ context, true, imis);
+ assertEquals(2, enabledImis.size());
+ for (int i = 0; i < enabledImis.size(); ++i) {
+ final InputMethodInfo imi = enabledImis.get(0);
+ // "DummyDefaultAutoVoiceIme" and "DummyDefaultEnKeyboardIme"
+ if (imi.getPackageName().equals("DummyDefaultAutoVoiceIme")
+ || imi.getPackageName().equals("DummyDefaultEnKeyboardIme")) {
+ continue;
+ } else {
+ fail("Invalid enabled subtype.");
+ }
+ }
+ }
+
+ @SmallTest
+ public void testDefaultEnabledImesWithOutDefaultVoiceIme() throws Exception {
+ final Context context = getInstrumentation().getTargetContext();
+ final ArrayList<InputMethodInfo> imis = new ArrayList<InputMethodInfo>();
+ imis.add(createNonDefaultAutoDummyVoiceIme0());
+ imis.add(createNonDefaultAutoDummyVoiceIme1());
+ imis.add(createNonDefaultDummyVoiceIme2());
+ imis.add(createDefaultDummyEnUSKeyboardIme());
+ imis.add(createNonDefaultDummyJaJPKeyboardIme());
+ final ArrayList<InputMethodInfo> enabledImis = InputMethodUtils.getDefaultEnabledImes(
+ context, true, imis);
+ assertEquals(3, enabledImis.size());
+ for (int i = 0; i < enabledImis.size(); ++i) {
+ final InputMethodInfo imi = enabledImis.get(0);
+ // "DummyNonDefaultAutoVoiceIme0", "DummyNonDefaultAutoVoiceIme1" and
+ // "DummyDefaultEnKeyboardIme"
+ if (imi.getPackageName().equals("DummyNonDefaultAutoVoiceIme0")
+ || imi.getPackageName().equals("DummyNonDefaultAutoVoiceIme1")
+ || imi.getPackageName().equals("DummyDefaultEnKeyboardIme")) {
+ continue;
+ } else {
+ fail("Invalid enabled subtype.");
+ }
+ }
+ }
+
+ private static InputMethodInfo createDummyInputMethodInfo(String packageName, String name,
+ CharSequence label, boolean isAuxIme, boolean isDefault,
+ List<InputMethodSubtype> subtypes) {
+ final ResolveInfo ri = new ResolveInfo();
+ final ServiceInfo si = new ServiceInfo();
+ final ApplicationInfo ai = new ApplicationInfo();
+ ai.packageName = packageName;
+ ai.enabled = true;
+ ai.flags |= ApplicationInfo.FLAG_SYSTEM;
+ si.applicationInfo = ai;
+ si.enabled = true;
+ si.packageName = packageName;
+ si.name = name;
+ si.exported = true;
+ si.nonLocalizedLabel = label;
+ ri.serviceInfo = si;
+ return new InputMethodInfo(ri, isAuxIme, "", subtypes, 1, isDefault);
+ }
+
+ private static InputMethodSubtype createDummyInputMethodSubtype(String locale, String mode,
+ boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype) {
+ return new InputMethodSubtype(0, 0, locale, mode, "", isAuxiliary,
+ overridesImplicitlyEnabledSubtype);
+ }
+
+ private static InputMethodInfo createDefaultAutoDummyVoiceIme() {
+ final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+ subtypes.add(createDummyInputMethodSubtype("auto", "voice", IS_AUX, IS_AUTO));
+ subtypes.add(createDummyInputMethodSubtype("en_US", "voice", IS_AUX, !IS_AUTO));
+ return createDummyInputMethodInfo("DummyDefaultAutoVoiceIme", "dummy.voice0",
+ "DummyVoice0", IS_AUX, IS_DEFAULT, subtypes);
+ }
+
+ private static InputMethodInfo createNonDefaultAutoDummyVoiceIme0() {
+ final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+ subtypes.add(createDummyInputMethodSubtype("auto", "voice", IS_AUX, IS_AUTO));
+ subtypes.add(createDummyInputMethodSubtype("en_US", "voice", IS_AUX, !IS_AUTO));
+ return createDummyInputMethodInfo("DummyNonDefaultAutoVoiceIme0", "dummy.voice1",
+ "DummyVoice1", IS_AUX, !IS_DEFAULT, subtypes);
+ }
+
+ private static InputMethodInfo createNonDefaultAutoDummyVoiceIme1() {
+ final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+ subtypes.add(createDummyInputMethodSubtype("auto", "voice", IS_AUX, IS_AUTO));
+ subtypes.add(createDummyInputMethodSubtype("en_US", "voice", IS_AUX, !IS_AUTO));
+ return createDummyInputMethodInfo("DummyNonDefaultAutoVoiceIme1", "dummy.voice2",
+ "DummyVoice2", IS_AUX, !IS_DEFAULT, subtypes);
+ }
+
+ private static InputMethodInfo createNonDefaultDummyVoiceIme2() {
+ final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+ subtypes.add(createDummyInputMethodSubtype("en_US", "voice", IS_AUX, !IS_AUTO));
+ return createDummyInputMethodInfo("DummyNonDefaultVoiceIme2", "dummy.voice3",
+ "DummyVoice3", IS_AUX, !IS_DEFAULT, subtypes);
+ }
+
+ private static InputMethodInfo createDefaultDummyEnUSKeyboardIme() {
+ final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+ subtypes.add(createDummyInputMethodSubtype("en_US", "keyboard", !IS_AUX, !IS_AUTO));
+ return createDummyInputMethodInfo("DummyDefaultEnKeyboardIme", "dummy.keyboard0",
+ "DummyKeyboard0", !IS_AUX, IS_DEFAULT, subtypes);
+ }
+
+ private static InputMethodInfo createNonDefaultDummyJaJPKeyboardIme() {
+ final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+ subtypes.add(createDummyInputMethodSubtype("ja_JP", "keyboard", !IS_AUX, !IS_AUTO));
+ return createDummyInputMethodInfo("DummyNonDefaultJaJPKeyboardIme", "dummy.keyboard1",
+ "DummyKeyboard1", !IS_AUX, !IS_DEFAULT, subtypes);
+ }
+}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 83ecdd9..cf04b5c 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -106,6 +106,10 @@
<group gid="net_bw_acct" />
</permission>
+ <permission name="android.permission.LOOP_RADIO" >
+ <group gid="loop_radio" />
+ </permission>
+
<!-- ================================================================== -->
<!-- ================================================================== -->
<!-- ================================================================== -->
diff --git a/graphics/java/android/renderscript/Matrix3f.java b/graphics/java/android/renderscript/Matrix3f.java
index 0bad7e0..5e9a7ca 100644
--- a/graphics/java/android/renderscript/Matrix3f.java
+++ b/graphics/java/android/renderscript/Matrix3f.java
@@ -140,7 +140,7 @@
mMat[4] = y*y*nc + c;
mMat[7] = yz*nc - xs;
mMat[2] = zx*nc - ys;
- mMat[6] = yz*nc + xs;
+ mMat[5] = yz*nc + xs;
mMat[8] = z*z*nc + c;
}
diff --git a/libs/hwui/DisplayListLogBuffer.cpp b/libs/hwui/DisplayListLogBuffer.cpp
index f204644..f039fcd 100644
--- a/libs/hwui/DisplayListLogBuffer.cpp
+++ b/libs/hwui/DisplayListLogBuffer.cpp
@@ -18,9 +18,8 @@
// BUFFER_SIZE size must be one more than a multiple of COMMAND_SIZE to ensure
// that mStart always points at the next command, not just the next item
-#define COMMAND_SIZE 2
#define NUM_COMMANDS 50
-#define BUFFER_SIZE ((NUM_COMMANDS * COMMAND_SIZE) + 1)
+#define BUFFER_SIZE ((NUM_COMMANDS) + 1)
/**
* DisplayListLogBuffer is a utility class which logs the most recent display
@@ -57,7 +56,7 @@
DisplayListLogBuffer::DisplayListLogBuffer() {
- mBufferFirst = (int*) malloc(BUFFER_SIZE * sizeof(int));
+ mBufferFirst = (OpLog*) malloc(BUFFER_SIZE * sizeof(OpLog));
mStart = mBufferFirst;
mBufferLast = mBufferFirst + BUFFER_SIZE - 1;
mEnd = mStart;
@@ -71,42 +70,30 @@
* Called from DisplayListRenderer to output the current buffer into the
* specified FILE. This only happens in a dumpsys/bugreport operation.
*/
-void DisplayListLogBuffer::outputCommands(FILE *file, const char* opNames[])
+void DisplayListLogBuffer::outputCommands(FILE *file)
{
- int *tmpBufferPtr = mStart;
+ OpLog* tmpBufferPtr = mStart;
while (true) {
if (tmpBufferPtr == mEnd) {
break;
}
- int level = *tmpBufferPtr++;
+ OpLog* nextOp = tmpBufferPtr++;
if (tmpBufferPtr > mBufferLast) {
tmpBufferPtr = mBufferFirst;
}
- int op = *tmpBufferPtr++;
- if (tmpBufferPtr > mBufferLast) {
- tmpBufferPtr = mBufferFirst;
- }
- uint32_t count = (level + 1) * 2;
- char indent[count + 1];
- for (uint32_t i = 0; i < count; i++) {
- indent[i] = ' ';
- }
- indent[count] = '\0';
- fprintf(file, "%s%s\n", indent, opNames[op]);
+
+ fprintf(file, "%*s%s\n", tmpBufferPtr->level*2, "", tmpBufferPtr->label);
}
}
-void DisplayListLogBuffer::writeCommand(int level, int op) {
- writeInt(level);
- writeInt(op);
-}
-
/**
- * Store the given value in the buffer and increment/wrap the mEnd
- * and mStart values as appropriate.
+ * Store the given level and label in the buffer and increment/wrap the mEnd
+ * and mStart values as appropriate. Label should point to static memory.
*/
-void DisplayListLogBuffer::writeInt(int value) {
- *((int*)mEnd) = value;
+void DisplayListLogBuffer::writeCommand(int level, const char* label) {
+ mEnd->level = level;
+ mEnd->label = label;
+
if (mEnd == mBufferLast) {
mEnd = mBufferFirst;
} else {
diff --git a/libs/hwui/DisplayListLogBuffer.h b/libs/hwui/DisplayListLogBuffer.h
index 5d689bb..c884789 100644
--- a/libs/hwui/DisplayListLogBuffer.h
+++ b/libs/hwui/DisplayListLogBuffer.h
@@ -31,19 +31,23 @@
friend class Singleton<DisplayListLogBuffer>;
public:
- void writeCommand(int level, int op);
- void writeInt(int value);
- void outputCommands(FILE *file, const char* opNames[]);
+ void writeCommand(int level, const char* label);
+ void outputCommands(FILE *file);
bool isEmpty() {
return (mStart == mEnd);
}
+ struct OpLog {
+ int level;
+ const char* label;
+ };
+
private:
- int *mBufferFirst; // where the memory starts
- int* mStart; // where the current command stream starts
- int* mEnd; // where the current commands end
- int* mBufferLast; // where the buffer memory ends
+ OpLog* mBufferFirst; // where the memory starts
+ OpLog* mStart; // where the current command stream starts
+ OpLog* mEnd; // where the current commands end
+ OpLog* mBufferLast; // where the buffer memory ends
};
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
new file mode 100644
index 0000000..a4de56d
--- /dev/null
+++ b/libs/hwui/DisplayListOp.h
@@ -0,0 +1,1138 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_DISPLAY_OPERATION_H
+#define ANDROID_HWUI_DISPLAY_OPERATION_H
+
+#include <SkXfermode.h>
+
+#include "OpenGLRenderer.h"
+#include "DisplayListRenderer.h"
+#include "utils/LinearAllocator.h"
+
+#define CRASH() do { \
+ *(int *)(uintptr_t)0xbbadbeef = 0; \
+ ((void(*)())0)(); /* More reliable, but doesn't say BBADBEEF */ \
+} while(false)
+
+#define MATRIX_STRING "[%.2f %.2f %.2f] [%.2f %.2f %.2f] [%.2f %.2f %.2f]"
+#define MATRIX_ARGS(m) \
+ m->get(0), m->get(1), m->get(2), \
+ m->get(3), m->get(4), m->get(5), \
+ m->get(6), m->get(7), m->get(8)
+#define RECT_STRING "%.2f %.2f %.2f %.2f"
+#define RECT_ARGS(r) \
+ r.left, r.top, r.right, r.bottom
+
+// Use OP_LOG for logging with arglist, OP_LOGS if just printing char*
+#define OP_LOGS(s) OP_LOG("%s", s)
+#define OP_LOG(s, ...) ALOGD( "%*s--%p " s, level * 2, "", this, __VA_ARGS__ )
+
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Structure for storing canvas operations when they are recorded into a DisplayList, so that they
+ * may be replayed to an OpenGLRenderer.
+ *
+ * To avoid individual memory allocations, DisplayListOps may only be allocated into a
+ * LinearAllocator's managed memory buffers. Each pointer held by a DisplayListOp is either a
+ * pointer into memory also allocated in the LinearAllocator (mostly for text and float buffers) or
+ * references a externally refcounted object (Sk... and Skia... objects). ~DisplayListOp() is
+ * never called as LinearAllocators are simply discarded, so no memory management should be done in
+ * this class.
+ */
+class DisplayListOp {
+public:
+ // These objects should always be allocated with a LinearAllocator, and never destroyed/deleted.
+ // standard new() intentionally not implemented, and delete/deconstructor should never be used.
+ virtual ~DisplayListOp() { CRASH(); }
+ static void operator delete(void* ptr) { CRASH(); }
+ /** static void* operator new(size_t size); PURPOSELY OMITTED **/
+ static void* operator new(size_t size, LinearAllocator& allocator) {
+ return allocator.alloc(size);
+ }
+
+ enum OpLogFlag {
+ kOpLogFlag_Recurse = 0x1,
+ kOpLogFlag_JSON = 0x2 // TODO: add?
+ };
+
+ //TODO: for draw batching, DrawOps should override a virtual sub-method, with
+ // DrawOps::apply deferring operations to a different list if possible
+ virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
+ uint32_t level, bool caching, int multipliedAlpha) = 0;
+
+ virtual void output(int level, uint32_t flags = 0) = 0;
+
+ // NOTE: it would be nice to declare constants and overriding the implementation in each op to
+ // point at the constants, but that seems to require a .cpp file
+ virtual const char* name() = 0;
+};
+
+class StateOp : public DisplayListOp {
+public:
+ StateOp() {};
+
+ virtual ~StateOp() {}
+
+ virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
+ uint32_t level, bool caching, int multipliedAlpha) {
+ applyState(renderer, saveCount);
+ return DrawGlInfo::kStatusDone;
+ }
+
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) = 0;
+};
+
+class DrawOp : public DisplayListOp {
+public:
+ DrawOp(SkPaint* paint)
+ : mPaint(paint), mQuickRejected(false) {}
+
+ virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
+ uint32_t level, bool caching, int multipliedAlpha) {
+ if (mQuickRejected && CC_LIKELY(flags & DisplayList::kReplayFlag_ClipChildren)) {
+ return DrawGlInfo::kStatusDone;
+ }
+
+ return applyDraw(renderer, dirty, level, caching, multipliedAlpha);
+ }
+
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+ bool caching, int multipliedAlpha) = 0;
+
+ // returns true if bounds exist
+ virtual bool getLocalBounds(Rect& localBounds) { return false; }
+
+ // TODO: better refine localbounds usage
+ void setQuickRejected(bool quickRejected) { mQuickRejected = quickRejected; }
+ bool getQuickRejected() { return mQuickRejected; }
+
+protected:
+ SkPaint* getPaint(OpenGLRenderer& renderer) {
+ return renderer.filterPaint(mPaint);
+ }
+
+ SkPaint* mPaint; // should be accessed via getPaint() when applying
+ bool mQuickRejected;
+};
+
+class DrawBoundedOp : public DrawOp {
+public:
+ DrawBoundedOp(float left, float top, float right, float bottom, SkPaint* paint)
+ : DrawOp(paint), mLocalBounds(left, top, right, bottom) {}
+
+ // default constructor for area, to be overridden in child constructor body
+ DrawBoundedOp(SkPaint* paint)
+ : DrawOp(paint) {}
+
+ bool getLocalBounds(Rect& localBounds) {
+ localBounds.set(mLocalBounds);
+ return true;
+ }
+
+protected:
+ Rect mLocalBounds; // displayed area in LOCAL coord. doesn't incorporate stroke, so check paint
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// STATE OPERATIONS - these may affect the state of the canvas/renderer, but do
+// not directly draw or alter output
+///////////////////////////////////////////////////////////////////////////////
+
+class SaveOp : public StateOp {
+public:
+ SaveOp(int flags)
+ : mFlags(flags) {}
+
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+ renderer.save(mFlags);
+ }
+
+ virtual void output(int level, uint32_t flags = 0) {
+ OP_LOG("Save flags %x", mFlags);
+ }
+
+ virtual const char* name() { return "Save"; }
+
+private:
+ int mFlags;
+};
+
+class RestoreToCountOp : public StateOp {
+public:
+ RestoreToCountOp(int count)
+ : mCount(count) {}
+
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+ renderer.restoreToCount(saveCount + mCount);
+
+ }
+
+ virtual void output(int level, uint32_t flags = 0) {
+ OP_LOG("Restore to count %d", mCount);
+ }
+
+ virtual const char* name() { return "RestoreToCount"; }
+
+private:
+ int mCount;
+};
+
+class SaveLayerOp : public StateOp {
+public:
+ SaveLayerOp(float left, float top, float right, float bottom, SkPaint* paint, int flags)
+ : mArea(left, top, right, bottom), mPaint(paint), mFlags(flags) {}
+
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+ SkPaint* paint = renderer.filterPaint(mPaint);
+ renderer.saveLayer(mArea.left, mArea.top, mArea.right, mArea.bottom, paint, mFlags);
+ }
+
+ virtual void output(int level, uint32_t flags = 0) {
+ OP_LOG("SaveLayer of area " RECT_STRING, RECT_ARGS(mArea));
+ }
+
+ virtual const char* name() { return "SaveLayer"; }
+
+private:
+ Rect mArea;
+ SkPaint* mPaint;
+ int mFlags;
+};
+
+class SaveLayerAlphaOp : public StateOp {
+public:
+ SaveLayerAlphaOp(float left, float top, float right, float bottom, int alpha, int flags)
+ : mArea(left, top, right, bottom), mAlpha(alpha), mFlags(flags) {}
+
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+ renderer.saveLayerAlpha(mArea.left, mArea.top, mArea.right, mArea.bottom, mAlpha, mFlags);
+ }
+
+ virtual void output(int level, uint32_t flags = 0) {
+ OP_LOG("SaveLayerAlpha of area " RECT_STRING, RECT_ARGS(mArea));
+ }
+
+ virtual const char* name() { return "SaveLayerAlpha"; }
+private:
+ Rect mArea;
+ int mAlpha;
+ int mFlags;
+};
+
+class TranslateOp : public StateOp {
+public:
+ TranslateOp(float dx, float dy)
+ : mDx(dx), mDy(dy) {}
+
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+ renderer.translate(mDx, mDy);
+ }
+
+ virtual void output(int level, uint32_t flags = 0) {
+ OP_LOG("Translate by %f %f", mDx, mDy);
+ }
+
+ virtual const char* name() { return "Translate"; }
+
+private:
+ float mDx;
+ float mDy;
+};
+
+class RotateOp : public StateOp {
+public:
+ RotateOp(float degrees)
+ : mDegrees(degrees) {}
+
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+ renderer.rotate(mDegrees);
+ }
+
+ virtual void output(int level, uint32_t flags = 0) {
+ OP_LOG("Rotate by %f degrees", mDegrees);
+ }
+
+ virtual const char* name() { return "Rotate"; }
+
+private:
+ float mDegrees;
+};
+
+class ScaleOp : public StateOp {
+public:
+ ScaleOp(float sx, float sy)
+ : mSx(sx), mSy(sy) {}
+
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+ renderer.scale(mSx, mSy);
+ }
+
+ virtual void output(int level, uint32_t flags = 0) {
+ OP_LOG("Scale by %f %f", mSx, mSy);
+ }
+
+ virtual const char* name() { return "Scale"; }
+
+private:
+ float mSx;
+ float mSy;
+};
+
+class SkewOp : public StateOp {
+public:
+ SkewOp(float sx, float sy)
+ : mSx(sx), mSy(sy) {}
+
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+ renderer.skew(mSx, mSy);
+ }
+
+ virtual void output(int level, uint32_t flags = 0) {
+ OP_LOG("Skew by %f %f", mSx, mSy);
+ }
+
+ virtual const char* name() { return "Skew"; }
+
+private:
+ float mSx;
+ float mSy;
+};
+
+class SetMatrixOp : public StateOp {
+public:
+ SetMatrixOp(SkMatrix* matrix)
+ : mMatrix(matrix) {}
+
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+ renderer.setMatrix(mMatrix);
+ }
+
+ virtual void output(int level, uint32_t flags = 0) {
+ OP_LOG("SetMatrix " MATRIX_STRING, MATRIX_ARGS(mMatrix));
+ }
+
+ virtual const char* name() { return "SetMatrix"; }
+
+private:
+ SkMatrix* mMatrix;
+};
+
+class ConcatMatrixOp : public StateOp {
+public:
+ ConcatMatrixOp(SkMatrix* matrix)
+ : mMatrix(matrix) {}
+
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+ renderer.concatMatrix(mMatrix);
+ }
+
+ virtual void output(int level, uint32_t flags = 0) {
+ OP_LOG("ConcatMatrix " MATRIX_STRING, MATRIX_ARGS(mMatrix));
+ }
+
+ virtual const char* name() { return "ConcatMatrix"; }
+
+private:
+ SkMatrix* mMatrix;
+};
+
+class ClipRectOp : public StateOp {
+public:
+ ClipRectOp(float left, float top, float right, float bottom, SkRegion::Op op)
+ : mArea(left, top, right, bottom), mOp(op) {}
+
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+ renderer.clipRect(mArea.left, mArea.top, mArea.right, mArea.bottom, mOp);
+ }
+
+ virtual void output(int level, uint32_t flags = 0) {
+ OP_LOG("ClipRect " RECT_STRING, RECT_ARGS(mArea));
+ }
+
+ virtual const char* name() { return "ClipRect"; }
+
+private:
+ Rect mArea;
+ SkRegion::Op mOp;
+};
+
+class ClipPathOp : public StateOp {
+public:
+ ClipPathOp(SkPath* path, SkRegion::Op op)
+ : mPath(path), mOp(op) {}
+
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+ renderer.clipPath(mPath, mOp);
+ }
+
+ virtual void output(int level, uint32_t flags = 0) {
+ SkRect bounds = mPath->getBounds();
+ OP_LOG("ClipPath bounds " RECT_STRING,
+ bounds.left(), bounds.top(), bounds.right(), bounds.bottom());
+ }
+
+ virtual const char* name() { return "ClipPath"; }
+
+private:
+ SkPath* mPath;
+ SkRegion::Op mOp;
+};
+
+class ClipRegionOp : public StateOp {
+public:
+ ClipRegionOp(SkRegion* region, SkRegion::Op op)
+ : mRegion(region), mOp(op) {}
+
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+ renderer.clipRegion(mRegion, mOp);
+ }
+
+ virtual void output(int level, uint32_t flags = 0) {
+ SkIRect bounds = mRegion->getBounds();
+ OP_LOG("ClipRegion bounds %d %d %d %d",
+ bounds.left(), bounds.top(), bounds.right(), bounds.bottom());
+ }
+
+ virtual const char* name() { return "ClipRegion"; }
+
+private:
+ SkRegion* mRegion;
+ SkRegion::Op mOp;
+};
+
+class ResetShaderOp : public StateOp {
+public:
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+ renderer.resetShader();
+ }
+
+ virtual void output(int level, uint32_t flags = 0) {
+ OP_LOGS("ResetShader");
+ }
+
+ virtual const char* name() { return "ResetShader"; }
+};
+
+class SetupShaderOp : public StateOp {
+public:
+ SetupShaderOp(SkiaShader* shader)
+ : mShader(shader) {}
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+ renderer.setupShader(mShader);
+ }
+
+ virtual void output(int level, uint32_t flags = 0) {
+ OP_LOG("SetupShader, shader %p", mShader);
+ }
+
+ virtual const char* name() { return "SetupShader"; }
+
+private:
+ SkiaShader* mShader;
+};
+
+class ResetColorFilterOp : public StateOp {
+public:
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+ renderer.resetColorFilter();
+ }
+
+ virtual void output(int level, uint32_t flags = 0) {
+ OP_LOGS("ResetColorFilter");
+ }
+
+ virtual const char* name() { return "ResetColorFilter"; }
+};
+
+class SetupColorFilterOp : public StateOp {
+public:
+ SetupColorFilterOp(SkiaColorFilter* colorFilter)
+ : mColorFilter(colorFilter) {}
+
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+ renderer.setupColorFilter(mColorFilter);
+ }
+
+ virtual void output(int level, uint32_t flags = 0) {
+ OP_LOG("SetupColorFilter, filter %p", mColorFilter);
+ }
+
+ virtual const char* name() { return "SetupColorFilter"; }
+
+private:
+ SkiaColorFilter* mColorFilter;
+};
+
+class ResetShadowOp : public StateOp {
+public:
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+ renderer.resetShadow();
+ }
+
+ virtual void output(int level, uint32_t flags = 0) {
+ OP_LOGS("ResetShadow");
+ }
+
+ virtual const char* name() { return "ResetShadow"; }
+};
+
+class SetupShadowOp : public StateOp {
+public:
+ SetupShadowOp(float radius, float dx, float dy, int color)
+ : mRadius(radius), mDx(dx), mDy(dy), mColor(color) {}
+
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+ renderer.setupShadow(mRadius, mDx, mDy, mColor);
+ }
+
+ virtual void output(int level, uint32_t flags = 0) {
+ OP_LOG("SetupShadow, radius %f, %f, %f, color %#x", mRadius, mDx, mDy, mColor);
+ }
+
+ virtual const char* name() { return "SetupShadow"; }
+
+private:
+ float mRadius;
+ float mDx;
+ float mDy;
+ int mColor;
+};
+
+class ResetPaintFilterOp : public StateOp {
+public:
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+ renderer.resetPaintFilter();
+ }
+
+ virtual void output(int level, uint32_t flags = 0) {
+ OP_LOGS("ResetPaintFilter");
+ }
+
+ virtual const char* name() { return "ResetPaintFilter"; }
+};
+
+class SetupPaintFilterOp : public StateOp {
+public:
+ SetupPaintFilterOp(int clearBits, int setBits)
+ : mClearBits(clearBits), mSetBits(setBits) {}
+
+ virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+ renderer.setupPaintFilter(mClearBits, mSetBits);
+ }
+
+ virtual void output(int level, uint32_t flags = 0) {
+ OP_LOG("SetupPaintFilter, clear %#x, set %#x", mClearBits, mSetBits);
+ }
+
+ virtual const char* name() { return "SetupPaintFilter"; }
+
+private:
+ int mClearBits;
+ int mSetBits;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// DRAW OPERATIONS - these are operations that can draw to the canvas's device
+///////////////////////////////////////////////////////////////////////////////
+
+class DrawBitmapOp : public DrawBoundedOp {
+public:
+ DrawBitmapOp(SkBitmap* bitmap, float left, float top, SkPaint* paint)
+ : DrawBoundedOp(left, top, left + bitmap->width(), top + bitmap->height(),
+ paint),
+ mBitmap(bitmap) {}
+
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+ bool caching, int multipliedAlpha) {
+ SkPaint* paint = getPaint(renderer);
+ int oldAlpha = -1;
+ if (caching && multipliedAlpha < 255) {
+ oldAlpha = paint->getAlpha();
+ paint->setAlpha(multipliedAlpha);
+ }
+ status_t ret = renderer.drawBitmap(mBitmap, mLocalBounds.left, mLocalBounds.top, paint);
+ if (oldAlpha >= 0) {
+ paint->setAlpha(oldAlpha);
+ }
+ return ret;
+ }
+
+ virtual void output(int level, uint32_t flags) {
+ OP_LOG("Draw bitmap %p at %f %f", mBitmap, mLocalBounds.left, mLocalBounds.top);
+ }
+
+ virtual const char* name() { return "DrawBitmap"; }
+
+protected:
+ SkBitmap* mBitmap;
+};
+
+class DrawBitmapMatrixOp : public DrawBoundedOp {
+public:
+ DrawBitmapMatrixOp(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint)
+ : DrawBoundedOp(paint), mBitmap(bitmap), mMatrix(matrix) {
+ mLocalBounds.set(0, 0, bitmap->width(), bitmap->height());
+ const mat4 transform(*matrix);
+ transform.mapRect(mLocalBounds);
+ }
+
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+ bool caching, int multipliedAlpha) {
+ return renderer.drawBitmap(mBitmap, mMatrix, getPaint(renderer));
+ }
+
+ virtual void output(int level, uint32_t flags) {
+ OP_LOG("Draw bitmap %p matrix " MATRIX_STRING, mBitmap, MATRIX_ARGS(mMatrix));
+ }
+
+ virtual const char* name() { return "DrawBitmap"; }
+
+private:
+ SkBitmap* mBitmap;
+ SkMatrix* mMatrix;
+};
+
+class DrawBitmapRectOp : public DrawBoundedOp {
+public:
+ DrawBitmapRectOp(SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom,
+ float dstLeft, float dstTop, float dstRight, float dstBottom, SkPaint* paint)
+ : DrawBoundedOp(dstLeft, dstTop, dstRight, dstBottom, paint),
+ mBitmap(bitmap), mSrc(srcLeft, srcTop, srcRight, srcBottom) {}
+
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+ bool caching, int multipliedAlpha) {
+ return renderer.drawBitmap(mBitmap, mSrc.left, mSrc.top, mSrc.right, mSrc.bottom,
+ mLocalBounds.left, mLocalBounds.top, mLocalBounds.right, mLocalBounds.bottom,
+ getPaint(renderer));
+ }
+
+ virtual void output(int level, uint32_t flags) {
+ OP_LOG("Draw bitmap %p src="RECT_STRING", dst="RECT_STRING,
+ mBitmap, RECT_ARGS(mSrc), RECT_ARGS(mLocalBounds));
+ }
+
+ virtual const char* name() { return "DrawBitmapRect"; }
+
+private:
+ SkBitmap* mBitmap;
+ Rect mSrc;
+};
+
+class DrawBitmapDataOp : public DrawBitmapOp {
+public:
+ DrawBitmapDataOp(SkBitmap* bitmap, float left, float top, SkPaint* paint)
+ : DrawBitmapOp(bitmap, left, top, paint) {}
+
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+ bool caching, int multipliedAlpha) {
+ return renderer.drawBitmapData(mBitmap, mLocalBounds.left,
+ mLocalBounds.top, getPaint(renderer));
+ }
+
+ virtual void output(int level, uint32_t flags) {
+ OP_LOG("Draw bitmap %p", mBitmap);
+ }
+
+ virtual const char* name() { return "DrawBitmapData"; }
+};
+
+class DrawBitmapMeshOp : public DrawOp {
+public:
+ DrawBitmapMeshOp(SkBitmap* bitmap, int meshWidth, int meshHeight,
+ float* vertices, int* colors, SkPaint* paint)
+ : DrawOp(paint), mBitmap(bitmap), mMeshWidth(meshWidth), mMeshHeight(meshHeight),
+ mVertices(vertices), mColors(colors) {}
+
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+ bool caching, int multipliedAlpha) {
+ return renderer.drawBitmapMesh(mBitmap, mMeshWidth, mMeshHeight,
+ mVertices, mColors, getPaint(renderer));
+ }
+
+ virtual void output(int level, uint32_t flags) {
+ OP_LOG("Draw bitmap %p mesh %d x %d", mBitmap, mMeshWidth, mMeshHeight);
+ }
+
+ virtual const char* name() { return "DrawBitmapMesh"; }
+
+private:
+ SkBitmap* mBitmap;
+ int mMeshWidth;
+ int mMeshHeight;
+ float* mVertices;
+ int* mColors;
+};
+
+class DrawPatchOp : public DrawBoundedOp {
+public:
+ DrawPatchOp(SkBitmap* bitmap, const int32_t* xDivs,
+ const int32_t* yDivs, const uint32_t* colors, uint32_t width, uint32_t height,
+ int8_t numColors, float left, float top, float right, float bottom,
+ int alpha, SkXfermode::Mode mode)
+ : DrawBoundedOp(left, top, right, bottom, 0),
+ mBitmap(bitmap), mxDivs(xDivs), myDivs(yDivs),
+ mColors(colors), mxDivsCount(width), myDivsCount(height),
+ mNumColors(numColors), mAlpha(alpha), mMode(mode) {};
+
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+ bool caching, int multipliedAlpha) {
+ // NOTE: not calling the virtual method, which takes a paint
+ return renderer.drawPatch(mBitmap, mxDivs, myDivs, mColors,
+ mxDivsCount, myDivsCount, mNumColors,
+ mLocalBounds.left, mLocalBounds.top,
+ mLocalBounds.right, mLocalBounds.bottom, mAlpha, mMode);
+ }
+
+ virtual void output(int level, uint32_t flags) {
+ OP_LOG("Draw patch "RECT_STRING, RECT_ARGS(mLocalBounds));
+ }
+
+ virtual const char* name() { return "DrawPatch"; }
+
+private:
+ SkBitmap* mBitmap;
+ const int32_t* mxDivs;
+ const int32_t* myDivs;
+ const uint32_t* mColors;
+ uint32_t mxDivsCount;
+ uint32_t myDivsCount;
+ int8_t mNumColors;
+ int mAlpha;
+ SkXfermode::Mode mMode;
+};
+
+class DrawColorOp : public DrawOp {
+public:
+ DrawColorOp(int color, SkXfermode::Mode mode)
+ : DrawOp(0), mColor(color), mMode(mode) {};
+
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+ bool caching, int multipliedAlpha) {
+ return renderer.drawColor(mColor, mMode);
+ }
+
+ virtual void output(int level, uint32_t flags) {
+ OP_LOG("Draw color %#x, mode %d", mColor, mMode);
+ }
+
+ virtual const char* name() { return "DrawColor"; }
+
+private:
+ int mColor;
+ SkXfermode::Mode mMode;
+};
+
+class DrawStrokableOp : public DrawBoundedOp {
+public:
+ DrawStrokableOp(float left, float top, float right, float bottom, SkPaint* paint)
+ : DrawBoundedOp(left, top, right, bottom, paint) {};
+
+ bool getLocalBounds(Rect& localBounds) {
+ if (mPaint && mPaint->getStyle() != SkPaint::kFill_Style) {
+ float outset = mPaint->getStrokeWidth() * 0.5f;
+ localBounds.set(mLocalBounds.left - outset, mLocalBounds.top - outset,
+ mLocalBounds.right + outset, mLocalBounds.bottom + outset);
+ } else {
+ localBounds.set(mLocalBounds);
+ }
+ return true;
+ }
+};
+
+class DrawRectOp : public DrawStrokableOp {
+public:
+ DrawRectOp(float left, float top, float right, float bottom, SkPaint* paint)
+ : DrawStrokableOp(left, top, right, bottom, paint) {}
+
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+ bool caching, int multipliedAlpha) {
+ return renderer.drawRect(mLocalBounds.left, mLocalBounds.top,
+ mLocalBounds.right, mLocalBounds.bottom, getPaint(renderer));
+ }
+
+ virtual void output(int level, uint32_t flags) {
+ OP_LOG("Draw Rect "RECT_STRING, RECT_ARGS(mLocalBounds));
+ }
+
+ virtual const char* name() { return "DrawRect"; }
+};
+
+class DrawRectsOp : public DrawOp {
+public:
+ DrawRectsOp(const float* rects, int count, SkPaint* paint)
+ : DrawOp(paint), mRects(rects), mCount(count) {}
+
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+ bool caching, int multipliedAlpha) {
+ return renderer.drawRects(mRects, mCount, getPaint(renderer));
+ }
+
+ virtual void output(int level, uint32_t flags) {
+ OP_LOG("Draw Rects count %d", mCount);
+ }
+
+ virtual const char* name() { return "DrawRects"; }
+
+private:
+ const float* mRects;
+ int mCount;
+};
+
+class DrawRoundRectOp : public DrawStrokableOp {
+public:
+ DrawRoundRectOp(float left, float top, float right, float bottom,
+ float rx, float ry, SkPaint* paint)
+ : DrawStrokableOp(left, top, right, bottom, paint), mRx(rx), mRy(ry) {}
+
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+ bool caching, int multipliedAlpha) {
+ return renderer.drawRoundRect(mLocalBounds.left, mLocalBounds.top,
+ mLocalBounds.right, mLocalBounds.bottom, mRx, mRy, getPaint(renderer));
+ }
+
+ virtual void output(int level, uint32_t flags) {
+ OP_LOG("Draw RoundRect "RECT_STRING", rx %f, ry %f", RECT_ARGS(mLocalBounds), mRx, mRy);
+ }
+
+ virtual const char* name() { return "DrawRoundRect"; }
+
+private:
+ float mRx;
+ float mRy;
+};
+
+class DrawCircleOp : public DrawStrokableOp {
+public:
+ DrawCircleOp(float x, float y, float radius, SkPaint* paint)
+ : DrawStrokableOp(x - radius, y - radius, x + radius, y + radius, paint),
+ mX(x), mY(y), mRadius(radius) {}
+
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+ bool caching, int multipliedAlpha) {
+ return renderer.drawCircle(mX, mY, mRadius, getPaint(renderer));
+ }
+
+ virtual void output(int level, uint32_t flags) {
+ OP_LOG("Draw Circle x %f, y %f, r %f", mX, mY, mRadius);
+ }
+
+ virtual const char* name() { return "DrawCircle"; }
+
+private:
+ float mX;
+ float mY;
+ float mRadius;
+};
+
+class DrawOvalOp : public DrawStrokableOp {
+public:
+ DrawOvalOp(float left, float top, float right, float bottom, SkPaint* paint)
+ : DrawStrokableOp(left, top, right, bottom, paint) {}
+
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+ bool caching, int multipliedAlpha) {
+ return renderer.drawOval(mLocalBounds.left, mLocalBounds.top,
+ mLocalBounds.right, mLocalBounds.bottom, getPaint(renderer));
+ }
+
+ virtual void output(int level, uint32_t flags) {
+ OP_LOG("Draw Oval "RECT_STRING, RECT_ARGS(mLocalBounds));
+ }
+
+ virtual const char* name() { return "DrawOval"; }
+};
+
+class DrawArcOp : public DrawStrokableOp {
+public:
+ DrawArcOp(float left, float top, float right, float bottom,
+ float startAngle, float sweepAngle, bool useCenter, SkPaint* paint)
+ : DrawStrokableOp(left, top, right, bottom, paint),
+ mStartAngle(startAngle), mSweepAngle(sweepAngle), mUseCenter(useCenter) {}
+
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+ bool caching, int multipliedAlpha) {
+ return renderer.drawArc(mLocalBounds.left, mLocalBounds.top,
+ mLocalBounds.right, mLocalBounds.bottom,
+ mStartAngle, mSweepAngle, mUseCenter, getPaint(renderer));
+ }
+
+ virtual void output(int level, uint32_t flags) {
+ OP_LOG("Draw Arc "RECT_STRING", start %f, sweep %f, useCenter %d",
+ RECT_ARGS(mLocalBounds), mStartAngle, mSweepAngle, mUseCenter);
+ }
+
+ virtual const char* name() { return "DrawArc"; }
+
+private:
+ float mStartAngle;
+ float mSweepAngle;
+ bool mUseCenter;
+};
+
+class DrawPathOp : public DrawBoundedOp {
+public:
+ DrawPathOp(SkPath* path, SkPaint* paint)
+ : DrawBoundedOp(paint), mPath(path) {
+ float left, top, offset;
+ uint32_t width, height;
+ computePathBounds(path, paint, left, top, offset, width, height);
+ left -= offset;
+ top -= offset;
+ mLocalBounds.set(left, top, left + width, top + height);
+ }
+
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+ bool caching, int multipliedAlpha) {
+ return renderer.drawPath(mPath, getPaint(renderer));
+ }
+
+ virtual void output(int level, uint32_t flags) {
+ OP_LOG("Draw Path %p in "RECT_STRING, mPath, RECT_ARGS(mLocalBounds));
+ }
+
+ virtual const char* name() { return "DrawPath"; }
+
+private:
+ SkPath* mPath;
+};
+
+class DrawLinesOp : public DrawOp {
+public:
+ DrawLinesOp(float* points, int count, SkPaint* paint)
+ : DrawOp(paint), mPoints(points), mCount(count) {
+ /* TODO: inherit from DrawBoundedOp and calculate localbounds something like:
+ for (int i = 0; i < count; i += 2) {
+ mLocalBounds.left = fminf(mLocalBounds.left, points[i]);
+ mLocalBounds.right = fmaxf(mLocalBounds.right, points[i]);
+ mLocalBounds.top = fminf(mLocalBounds.top, points[i+1]);
+ mLocalBounds.bottom = fmaxf(mLocalBounds.bottom, points[i+1]);
+ }
+ */
+ }
+
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+ bool caching, int multipliedAlpha) {
+ return renderer.drawLines(mPoints, mCount, getPaint(renderer));
+ }
+
+ virtual void output(int level, uint32_t flags) {
+ OP_LOG("Draw Lines count %d", mCount);
+ }
+
+ virtual const char* name() { return "DrawLines"; }
+
+protected:
+ float* mPoints;
+ int mCount;
+};
+
+class DrawPointsOp : public DrawLinesOp {
+public:
+ DrawPointsOp(float* points, int count, SkPaint* paint)
+ : DrawLinesOp(points, count, paint) {}
+
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+ bool caching, int multipliedAlpha) {
+ return renderer.drawPoints(mPoints, mCount, getPaint(renderer));
+ }
+
+ virtual void output(int level, uint32_t flags) {
+ OP_LOG("Draw Points count %d", mCount);
+ }
+
+ virtual const char* name() { return "DrawPoints"; }
+};
+
+class DrawSomeTextOp : public DrawOp {
+public:
+ DrawSomeTextOp(const char* text, int bytesCount, int count, SkPaint* paint)
+ : DrawOp(paint), mText(text), mBytesCount(bytesCount), mCount(count) {};
+
+ virtual void output(int level, uint32_t flags) {
+ OP_LOG("Draw some text, %d bytes", mBytesCount);
+ }
+protected:
+ const char* mText;
+ int mBytesCount;
+ int mCount;
+};
+
+class DrawTextOnPathOp : public DrawSomeTextOp {
+public:
+ DrawTextOnPathOp(const char* text, int bytesCount, int count,
+ SkPath* path, float hOffset, float vOffset, SkPaint* paint)
+ : DrawSomeTextOp(text, bytesCount, count, paint),
+ mPath(path), mHOffset(hOffset), mVOffset(vOffset) {
+ /* TODO: inherit from DrawBounded and init mLocalBounds */
+ }
+
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+ bool caching, int multipliedAlpha) {
+ return renderer.drawTextOnPath(mText, mBytesCount, mCount, mPath,
+ mHOffset, mVOffset, getPaint(renderer));
+ }
+
+ virtual const char* name() { return "DrawTextOnPath"; }
+
+private:
+ SkPath* mPath;
+ float mHOffset;
+ float mVOffset;
+};
+
+class DrawPosTextOp : public DrawSomeTextOp {
+public:
+ DrawPosTextOp(const char* text, int bytesCount, int count,
+ const float* positions, SkPaint* paint)
+ : DrawSomeTextOp(text, bytesCount, count, paint), mPositions(positions) {
+ /* TODO: inherit from DrawBounded and init mLocalBounds */
+ }
+
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+ bool caching, int multipliedAlpha) {
+ return renderer.drawPosText(mText, mBytesCount, mCount, mPositions, getPaint(renderer));
+ }
+
+ virtual const char* name() { return "DrawPosText"; }
+
+private:
+ const float* mPositions;
+};
+
+class DrawTextOp : public DrawBoundedOp {
+public:
+ DrawTextOp(const char* text, int bytesCount, int count, float x, float y,
+ const float* positions, SkPaint* paint, float length)
+ : DrawBoundedOp(paint), mText(text), mBytesCount(bytesCount), mCount(count),
+ mX(x), mY(y), mPositions(positions), mLength(length) {
+ SkPaint::FontMetrics metrics;
+ paint->getFontMetrics(&metrics, 0.0f);
+ mLocalBounds.set(mX, mY + metrics.fTop, mX + length, mY + metrics.fBottom);
+ }
+
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+ bool caching, int multipliedAlpha) {
+ return renderer.drawText(mText, mBytesCount, mCount, mX, mY,
+ mPositions, getPaint(renderer), mLength);
+ }
+
+ virtual void output(int level, uint32_t flags) {
+ OP_LOG("Draw Text of count %d, bytes %d", mCount, mBytesCount);
+ }
+
+ virtual const char* name() { return "DrawText"; }
+
+private:
+ const char* mText;
+ int mBytesCount;
+ int mCount;
+ float mX;
+ float mY;
+ const float* mPositions;
+ float mLength;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// SPECIAL DRAW OPERATIONS
+///////////////////////////////////////////////////////////////////////////////
+
+class DrawFunctorOp : public DrawOp {
+public:
+ DrawFunctorOp(Functor* functor)
+ : DrawOp(0), mFunctor(functor) {}
+
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+ bool caching, int multipliedAlpha) {
+ renderer.startMark("GL functor");
+ status_t ret = renderer.callDrawGLFunction(mFunctor, dirty);
+ renderer.endMark();
+ return ret;
+ }
+
+ virtual void output(int level, uint32_t flags) {
+ OP_LOG("Draw Functor %p", mFunctor);
+ }
+
+ virtual const char* name() { return "DrawFunctor"; }
+
+private:
+ Functor* mFunctor;
+};
+
+class DrawDisplayListOp : public DrawOp {
+public:
+ DrawDisplayListOp(DisplayList* displayList, int flags)
+ : DrawOp(0), mDisplayList(displayList), mFlags(flags) {}
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+ bool caching, int multipliedAlpha) {
+ return renderer.drawDisplayList(mDisplayList, dirty, mFlags, level + 1);
+ }
+
+ virtual void output(int level, uint32_t flags) {
+ OP_LOG("Draw Display List %p, flags %#x", mDisplayList, mFlags);
+ if (mDisplayList && (flags & kOpLogFlag_Recurse)) {
+ mDisplayList->output(level + 1);
+ }
+ }
+
+ virtual const char* name() { return "DrawDisplayList"; }
+
+private:
+ DisplayList* mDisplayList;
+ int mFlags;
+};
+
+class DrawLayerOp : public DrawOp {
+public:
+ DrawLayerOp(Layer* layer, float x, float y, SkPaint* paint)
+ : DrawOp(paint), mLayer(layer), mX(x), mY(y) {}
+
+ virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
+ bool caching, int multipliedAlpha) {
+ int oldAlpha = -1;
+
+ if (caching && multipliedAlpha < 255) {
+ oldAlpha = mLayer->getAlpha();
+ mLayer->setAlpha(multipliedAlpha);
+ }
+ status_t ret = renderer.drawLayer(mLayer, mX, mY, getPaint(renderer));
+ if (oldAlpha >= 0) {
+ mLayer->setAlpha(oldAlpha);
+ }
+ return ret;
+ }
+
+ virtual void output(int level, uint32_t flags) {
+ OP_LOG("Draw Layer %p at %f %f", mLayer, mX, mY);
+ }
+
+ virtual const char* name() { return "DrawLayer"; }
+
+private:
+ Layer* mLayer;
+ float mX;
+ float mY;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_DISPLAY_OPERATION_H
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index f0c9ce4..5cb629e 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -21,6 +21,7 @@
#include <private/hwui/DrawGlInfo.h>
#include "DisplayListLogBuffer.h"
+#include "DisplayListOp.h"
#include "DisplayListRenderer.h"
#include "Caches.h"
@@ -31,53 +32,6 @@
// Display list
///////////////////////////////////////////////////////////////////////////////
-const char* DisplayList::OP_NAMES[] = {
- "Save",
- "Restore",
- "RestoreToCount",
- "SaveLayer",
- "SaveLayerAlpha",
- "Translate",
- "Rotate",
- "Scale",
- "Skew",
- "SetMatrix",
- "ConcatMatrix",
- "ClipRect",
- "ClipPath",
- "ClipRegion",
- "DrawDisplayList",
- "DrawLayer",
- "DrawBitmap",
- "DrawBitmapMatrix",
- "DrawBitmapRect",
- "DrawBitmapData",
- "DrawBitmapMesh",
- "DrawPatch",
- "DrawColor",
- "DrawRect",
- "DrawRoundRect",
- "DrawCircle",
- "DrawOval",
- "DrawArc",
- "DrawPath",
- "DrawLines",
- "DrawPoints",
- "DrawTextOnPath",
- "DrawPosText",
- "DrawText",
- "DrawRects",
- "ResetShader",
- "SetupShader",
- "ResetColorFilter",
- "SetupColorFilter",
- "ResetShadow",
- "SetupShadow",
- "ResetPaintFilter",
- "SetupPaintFilter",
- "DrawGLFunction"
-};
-
void DisplayList::outputLogBuffer(int fd) {
DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
if (logBuffer.isEmpty()) {
@@ -87,7 +41,7 @@
FILE *file = fdopen(fd, "a");
fprintf(file, "\nRecent DisplayList operations\n");
- logBuffer.outputCommands(file, OP_NAMES);
+ logBuffer.outputCommands(file);
String8 cachesLog;
Caches::getInstance().dumpMemoryUsage(cachesLog);
@@ -116,9 +70,7 @@
}
void DisplayList::clearResources() {
- sk_free((void*) mReader.base());
- mReader.setMemory(NULL, 0);
-
+ mDisplayListData = NULL;
delete mTransformMatrix;
delete mTransformCamera;
delete mTransformMatrix3D;
@@ -200,7 +152,6 @@
}
void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing) {
-
if (reusing) {
// re-using display list - clear out previous allocations
clearResources();
@@ -208,16 +159,13 @@
init();
- const SkWriter32& writer = recorder.writeStream();
- if (writer.size() == 0) {
+ mDisplayListData = recorder.getDisplayListData();
+ mSize = mDisplayListData->allocator.usedSize();
+
+ if (mSize == 0) {
return;
}
- mSize = writer.size();
- void* buffer = sk_malloc_throw(mSize);
- writer.flatten(buffer);
- mReader.setMemory(buffer, mSize);
-
mFunctorCount = recorder.getFunctorCount();
Caches& caches = Caches::getInstance();
@@ -312,392 +260,17 @@
* This function is a simplified version of replay(), where we simply retrieve and log the
* display list. This function should remain in sync with the replay() function.
*/
-void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) {
- TextContainer text;
-
- uint32_t count = (level + 1) * 2;
- char indent[count + 1];
- for (uint32_t i = 0; i < count; i++) {
- indent[i] = ' ';
- }
- indent[count] = '\0';
- ALOGD("%sStart display list (%p, %s, render=%d)", (char*) indent + 2, this,
+void DisplayList::output(uint32_t level) {
+ ALOGD("%*sStart display list (%p, %s, render=%d)", level * 2, "", this,
mName.string(), isRenderable());
- ALOGD("%s%s %d", indent, "Save", SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
- int saveCount = renderer.getSaveCount() - 1;
-
- outputViewProperties(renderer, (char*) indent);
- mReader.rewind();
-
- while (!mReader.eof()) {
- int op = mReader.readInt();
- if (op & OP_MAY_BE_SKIPPED_MASK) {
- int skip = mReader.readInt();
- ALOGD("%sSkip %d", (char*) indent, skip);
- op &= ~OP_MAY_BE_SKIPPED_MASK;
- }
-
- switch (op) {
- case DrawGLFunction: {
- Functor *functor = (Functor *) getInt();
- ALOGD("%s%s %p", (char*) indent, OP_NAMES[op], functor);
- }
- break;
- case Save: {
- int rendererNum = getInt();
- ALOGD("%s%s %d", (char*) indent, OP_NAMES[op], rendererNum);
- }
- break;
- case Restore: {
- ALOGD("%s%s", (char*) indent, OP_NAMES[op]);
- }
- break;
- case RestoreToCount: {
- int restoreCount = saveCount + getInt();
- ALOGD("%s%s %d", (char*) indent, OP_NAMES[op], restoreCount);
- }
- break;
- case SaveLayer: {
- float f1 = getFloat();
- float f2 = getFloat();
- float f3 = getFloat();
- float f4 = getFloat();
- SkPaint* paint = getPaint(renderer);
- int flags = getInt();
- ALOGD("%s%s %.2f, %.2f, %.2f, %.2f, %p, 0x%x", (char*) indent,
- OP_NAMES[op], f1, f2, f3, f4, paint, flags);
- }
- break;
- case SaveLayerAlpha: {
- float f1 = getFloat();
- float f2 = getFloat();
- float f3 = getFloat();
- float f4 = getFloat();
- int alpha = getInt();
- int flags = getInt();
- ALOGD("%s%s %.2f, %.2f, %.2f, %.2f, %d, 0x%x", (char*) indent,
- OP_NAMES[op], f1, f2, f3, f4, alpha, flags);
- }
- break;
- case Translate: {
- float f1 = getFloat();
- float f2 = getFloat();
- ALOGD("%s%s %.2f, %.2f", (char*) indent, OP_NAMES[op], f1, f2);
- }
- break;
- case Rotate: {
- float rotation = getFloat();
- ALOGD("%s%s %.2f", (char*) indent, OP_NAMES[op], rotation);
- }
- break;
- case Scale: {
- float sx = getFloat();
- float sy = getFloat();
- ALOGD("%s%s %.2f, %.2f", (char*) indent, OP_NAMES[op], sx, sy);
- }
- break;
- case Skew: {
- float sx = getFloat();
- float sy = getFloat();
- ALOGD("%s%s %.2f, %.2f", (char*) indent, OP_NAMES[op], sx, sy);
- }
- break;
- case SetMatrix: {
- SkMatrix* matrix = getMatrix();
- ALOGD("%s%s %p", (char*) indent, OP_NAMES[op], matrix);
- }
- break;
- case ConcatMatrix: {
- SkMatrix* matrix = getMatrix();
- ALOGD("%s%s new concat %p: [%f, %f, %f] [%f, %f, %f] [%f, %f, %f]",
- (char*) indent, OP_NAMES[op], matrix, matrix->get(0), matrix->get(1),
- matrix->get(2), matrix->get(3), matrix->get(4), matrix->get(5),
- matrix->get(6), matrix->get(7), matrix->get(8));
- }
- break;
- case ClipRect: {
- float f1 = getFloat();
- float f2 = getFloat();
- float f3 = getFloat();
- float f4 = getFloat();
- int regionOp = getInt();
- ALOGD("%s%s %.2f, %.2f, %.2f, %.2f, %d", (char*) indent, OP_NAMES[op],
- f1, f2, f3, f4, regionOp);
- }
- break;
- case ClipPath: {
- SkPath* path = getPath();
- int regionOp = getInt();
- ALOGD("%s%s %d", (char*) indent, OP_NAMES[op], regionOp);
- }
- break;
- case ClipRegion: {
- SkRegion* region = getRegion();
- int regionOp = getInt();
- ALOGD("%s%s %d", (char*) indent, OP_NAMES[op], regionOp);
- }
- break;
- case DrawDisplayList: {
- DisplayList* displayList = getDisplayList();
- int32_t flags = getInt();
- ALOGD("%s%s %p, %dx%d, 0x%x %d", (char*) indent, OP_NAMES[op],
- displayList, mWidth, mHeight, flags, level + 1);
- renderer.outputDisplayList(displayList, level + 1);
- }
- break;
- case DrawLayer: {
- Layer* layer = (Layer*) getInt();
- float x = getFloat();
- float y = getFloat();
- SkPaint* paint = getPaint(renderer);
- ALOGD("%s%s %p, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op],
- layer, x, y, paint);
- }
- break;
- case DrawBitmap: {
- SkBitmap* bitmap = getBitmap();
- float x = getFloat();
- float y = getFloat();
- SkPaint* paint = getPaint(renderer);
- ALOGD("%s%s %p, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op],
- bitmap, x, y, paint);
- }
- break;
- case DrawBitmapMatrix: {
- SkBitmap* bitmap = getBitmap();
- SkMatrix* matrix = getMatrix();
- SkPaint* paint = getPaint(renderer);
- ALOGD("%s%s %p, %p, %p", (char*) indent, OP_NAMES[op],
- bitmap, matrix, paint);
- }
- break;
- case DrawBitmapRect: {
- SkBitmap* bitmap = getBitmap();
- float f1 = getFloat();
- float f2 = getFloat();
- float f3 = getFloat();
- float f4 = getFloat();
- float f5 = getFloat();
- float f6 = getFloat();
- float f7 = getFloat();
- float f8 = getFloat();
- SkPaint* paint = getPaint(renderer);
- ALOGD("%s%s %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %p",
- (char*) indent, OP_NAMES[op], bitmap, f1, f2, f3, f4, f5, f6, f7, f8, paint);
- }
- break;
- case DrawBitmapData: {
- SkBitmap* bitmap = getBitmapData();
- float x = getFloat();
- float y = getFloat();
- SkPaint* paint = getPaint(renderer);
- ALOGD("%s%s %.2f, %.2f, %p", (char*) indent, OP_NAMES[op], x, y, paint);
- }
- break;
- case DrawBitmapMesh: {
- int verticesCount = 0;
- uint32_t colorsCount = 0;
- SkBitmap* bitmap = getBitmap();
- uint32_t meshWidth = getInt();
- uint32_t meshHeight = getInt();
- float* vertices = getFloats(verticesCount);
- bool hasColors = getInt();
- int* colors = hasColors ? getInts(colorsCount) : NULL;
- SkPaint* paint = getPaint(renderer);
- ALOGD("%s%s", (char*) indent, OP_NAMES[op]);
- }
- break;
- case DrawPatch: {
- int32_t* xDivs = NULL;
- int32_t* yDivs = NULL;
- uint32_t* colors = NULL;
- uint32_t xDivsCount = 0;
- uint32_t yDivsCount = 0;
- int8_t numColors = 0;
- SkBitmap* bitmap = getBitmap();
- xDivs = getInts(xDivsCount);
- yDivs = getInts(yDivsCount);
- colors = getUInts(numColors);
- float left = getFloat();
- float top = getFloat();
- float right = getFloat();
- float bottom = getFloat();
- int alpha = getInt();
- SkXfermode::Mode mode = (SkXfermode::Mode) getInt();
- ALOGD("%s%s %.2f, %.2f, %.2f, %.2f", (char*) indent, OP_NAMES[op],
- left, top, right, bottom);
- }
- break;
- case DrawColor: {
- int color = getInt();
- int xferMode = getInt();
- ALOGD("%s%s 0x%x %d", (char*) indent, OP_NAMES[op], color, xferMode);
- }
- break;
- case DrawRect: {
- float f1 = getFloat();
- float f2 = getFloat();
- float f3 = getFloat();
- float f4 = getFloat();
- SkPaint* paint = getPaint(renderer);
- ALOGD("%s%s %.2f, %.2f, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op],
- f1, f2, f3, f4, paint);
- }
- break;
- case DrawRoundRect: {
- float f1 = getFloat();
- float f2 = getFloat();
- float f3 = getFloat();
- float f4 = getFloat();
- float f5 = getFloat();
- float f6 = getFloat();
- SkPaint* paint = getPaint(renderer);
- ALOGD("%s%s %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %p",
- (char*) indent, OP_NAMES[op], f1, f2, f3, f4, f5, f6, paint);
- }
- break;
- case DrawCircle: {
- float f1 = getFloat();
- float f2 = getFloat();
- float f3 = getFloat();
- SkPaint* paint = getPaint(renderer);
- ALOGD("%s%s %.2f, %.2f, %.2f, %p",
- (char*) indent, OP_NAMES[op], f1, f2, f3, paint);
- }
- break;
- case DrawOval: {
- float f1 = getFloat();
- float f2 = getFloat();
- float f3 = getFloat();
- float f4 = getFloat();
- SkPaint* paint = getPaint(renderer);
- ALOGD("%s%s %.2f, %.2f, %.2f, %.2f, %p",
- (char*) indent, OP_NAMES[op], f1, f2, f3, f4, paint);
- }
- break;
- case DrawArc: {
- float f1 = getFloat();
- float f2 = getFloat();
- float f3 = getFloat();
- float f4 = getFloat();
- float f5 = getFloat();
- float f6 = getFloat();
- int i1 = getInt();
- SkPaint* paint = getPaint(renderer);
- ALOGD("%s%s %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p",
- (char*) indent, OP_NAMES[op], f1, f2, f3, f4, f5, f6, i1, paint);
- }
- break;
- case DrawPath: {
- SkPath* path = getPath();
- SkPaint* paint = getPaint(renderer);
- ALOGD("%s%s %p, %p", (char*) indent, OP_NAMES[op], path, paint);
- }
- break;
- case DrawLines: {
- int count = 0;
- float* points = getFloats(count);
- SkPaint* paint = getPaint(renderer);
- ALOGD("%s%s", (char*) indent, OP_NAMES[op]);
- }
- break;
- case DrawPoints: {
- int count = 0;
- float* points = getFloats(count);
- SkPaint* paint = getPaint(renderer);
- ALOGD("%s%s", (char*) indent, OP_NAMES[op]);
- }
- break;
- case DrawTextOnPath: {
- getText(&text);
- int32_t count = getInt();
- SkPath* path = getPath();
- float hOffset = getFloat();
- float vOffset = getFloat();
- SkPaint* paint = getPaint(renderer);
- ALOGD("%s%s %s, %d, %d, %p", (char*) indent, OP_NAMES[op],
- text.text(), text.length(), count, paint);
- }
- break;
- case DrawPosText: {
- getText(&text);
- int count = getInt();
- int positionsCount = 0;
- float* positions = getFloats(positionsCount);
- SkPaint* paint = getPaint(renderer);
- ALOGD("%s%s %s, %d, %d, %p", (char*) indent, OP_NAMES[op],
- text.text(), text.length(), count, paint);
- }
- break;
- case DrawText: {
- getText(&text);
- int32_t count = getInt();
- float x = getFloat();
- float y = getFloat();
- int32_t positionsCount = 0;
- float* positions = getFloats(positionsCount);
- SkPaint* paint = getPaint(renderer);
- float length = getFloat();
- ALOGD("%s%s %s, %d, %d, %p", (char*) indent, OP_NAMES[op],
- text.text(), text.length(), count, paint);
- }
- break;
- case DrawRects: {
- int32_t count = 0;
- float* rects = getFloats(count);
- SkPaint* paint = getPaint(renderer);
- ALOGD("%s%s %d, %p", (char*) indent, OP_NAMES[op], count / 4, paint);
- }
- break;
- case ResetShader: {
- ALOGD("%s%s", (char*) indent, OP_NAMES[op]);
- }
- break;
- case SetupShader: {
- SkiaShader* shader = getShader();
- ALOGD("%s%s %p", (char*) indent, OP_NAMES[op], shader);
- }
- break;
- case ResetColorFilter: {
- ALOGD("%s%s", (char*) indent, OP_NAMES[op]);
- }
- break;
- case SetupColorFilter: {
- SkiaColorFilter *colorFilter = getColorFilter();
- ALOGD("%s%s %p", (char*) indent, OP_NAMES[op], colorFilter);
- }
- break;
- case ResetShadow: {
- ALOGD("%s%s", (char*) indent, OP_NAMES[op]);
- }
- break;
- case SetupShadow: {
- float radius = getFloat();
- float dx = getFloat();
- float dy = getFloat();
- int color = getInt();
- ALOGD("%s%s %.2f, %.2f, %.2f, 0x%x", (char*) indent, OP_NAMES[op],
- radius, dx, dy, color);
- }
- break;
- case ResetPaintFilter: {
- ALOGD("%s%s", (char*) indent, OP_NAMES[op]);
- }
- break;
- case SetupPaintFilter: {
- int clearBits = getInt();
- int setBits = getInt();
- ALOGD("%s%s 0x%x, 0x%x", (char*) indent, OP_NAMES[op], clearBits, setBits);
- }
- break;
- default:
- ALOGD("Display List error: op not handled: %s%s",
- (char*) indent, OP_NAMES[op]);
- break;
- }
+ ALOGD("%*s%s %d", level * 4, "", "Save", SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ outputViewProperties(level);
+ int flags = DisplayListOp::kOpLogFlag_Recurse;
+ for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
+ mDisplayListData->displayListOps[i]->output(level, flags);
}
- ALOGD("%sDone (%p, %s)", (char*) indent + 2, this, mName.string());
+ ALOGD("%*sDone (%p, %s)", level * 2, "", this, mName.string());
}
void DisplayList::updateMatrix() {
@@ -743,115 +316,68 @@
}
}
-void DisplayList::outputViewProperties(OpenGLRenderer& renderer, char* indent) {
+void DisplayList::outputViewProperties(uint32_t level) {
updateMatrix();
if (mLeft != 0 || mTop != 0) {
- ALOGD("%s%s %d, %d", indent, "Translate (left, top)", mLeft, mTop);
+ ALOGD("%*sTranslate (left, top) %d, %d", level * 2, "", mLeft, mTop);
}
if (mStaticMatrix) {
- ALOGD("%s%s %p: [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f]",
- indent, "ConcatMatrix (static)", mStaticMatrix,
- mStaticMatrix->get(0), mStaticMatrix->get(1),
- mStaticMatrix->get(2), mStaticMatrix->get(3),
- mStaticMatrix->get(4), mStaticMatrix->get(5),
- mStaticMatrix->get(6), mStaticMatrix->get(7),
- mStaticMatrix->get(8));
+ ALOGD("%*sConcatMatrix (static) %p: " MATRIX_STRING,
+ level * 2, "", mStaticMatrix, MATRIX_ARGS(mStaticMatrix));
}
if (mAnimationMatrix) {
- ALOGD("%s%s %p: [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f]",
- indent, "ConcatMatrix (animation)", mAnimationMatrix,
- mAnimationMatrix->get(0), mAnimationMatrix->get(1),
- mAnimationMatrix->get(2), mAnimationMatrix->get(3),
- mAnimationMatrix->get(4), mAnimationMatrix->get(5),
- mAnimationMatrix->get(6), mAnimationMatrix->get(7),
- mAnimationMatrix->get(8));
+ ALOGD("%*sConcatMatrix (animation) %p: " MATRIX_STRING,
+ level * 2, "", mAnimationMatrix, MATRIX_ARGS(mStaticMatrix));
}
if (mMatrixFlags != 0) {
if (mMatrixFlags == TRANSLATION) {
- ALOGD("%s%s %f, %f", indent, "Translate", mTranslationX, mTranslationY);
+ ALOGD("%*sTranslate %f, %f", level * 2, "", mTranslationX, mTranslationY);
} else {
- ALOGD("%s%s %p: [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f]",
- indent, "ConcatMatrix", mTransformMatrix,
- mTransformMatrix->get(0), mTransformMatrix->get(1),
- mTransformMatrix->get(2), mTransformMatrix->get(3),
- mTransformMatrix->get(4), mTransformMatrix->get(5),
- mTransformMatrix->get(6), mTransformMatrix->get(7),
- mTransformMatrix->get(8));
+ ALOGD("%*sConcatMatrix %p: " MATRIX_STRING,
+ level * 2, "", mTransformMatrix, MATRIX_ARGS(mTransformMatrix));
}
}
if (mAlpha < 1 && !mCaching) {
if (!mHasOverlappingRendering) {
- ALOGD("%s%s %.2f", indent, "SetAlpha", mAlpha);
+ ALOGD("%*sSetAlpha %.2f", level * 2, "", mAlpha);
} else {
int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
if (mClipChildren) {
flags |= SkCanvas::kClipToLayer_SaveFlag;
}
- ALOGD("%s%s %.2f, %.2f, %.2f, %.2f, %d, 0x%x", indent, "SaveLayerAlpha",
+ ALOGD("%*sSaveLayerAlpha %.2f, %.2f, %.2f, %.2f, %d, 0x%x", level * 2, "",
(float) 0, (float) 0, (float) mRight - mLeft, (float) mBottom - mTop,
mMultipliedAlpha, flags);
}
}
if (mClipChildren) {
- ALOGD("%s%s %.2f, %.2f, %.2f, %.2f", indent, "ClipRect", 0.0f, 0.0f,
+ ALOGD("%*sClipRect %.2f, %.2f, %.2f, %.2f", level * 2, "", 0.0f, 0.0f,
(float) mRight - mLeft, (float) mBottom - mTop);
}
}
void DisplayList::setViewProperties(OpenGLRenderer& renderer, uint32_t level) {
-#if DEBUG_DISPLAY_LIST
- uint32_t count = (level + 1) * 2;
- char indent[count + 1];
- for (uint32_t i = 0; i < count; i++) {
- indent[i] = ' ';
- }
- indent[count] = '\0';
+#if DEBUG_DISPLAYLIST
+ outputViewProperties(level);
#endif
updateMatrix();
if (mLeft != 0 || mTop != 0) {
- DISPLAY_LIST_LOGD("%s%s %d, %d", indent, "Translate (left, top)", mLeft, mTop);
renderer.translate(mLeft, mTop);
}
if (mStaticMatrix) {
- DISPLAY_LIST_LOGD(
- "%s%s %p: [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f]",
- indent, "ConcatMatrix (static)", mStaticMatrix,
- mStaticMatrix->get(0), mStaticMatrix->get(1),
- mStaticMatrix->get(2), mStaticMatrix->get(3),
- mStaticMatrix->get(4), mStaticMatrix->get(5),
- mStaticMatrix->get(6), mStaticMatrix->get(7),
- mStaticMatrix->get(8));
renderer.concatMatrix(mStaticMatrix);
} else if (mAnimationMatrix) {
- DISPLAY_LIST_LOGD(
- "%s%s %p: [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f]",
- indent, "ConcatMatrix (animation)", mAnimationMatrix,
- mAnimationMatrix->get(0), mAnimationMatrix->get(1),
- mAnimationMatrix->get(2), mAnimationMatrix->get(3),
- mAnimationMatrix->get(4), mAnimationMatrix->get(5),
- mAnimationMatrix->get(6), mAnimationMatrix->get(7),
- mAnimationMatrix->get(8));
renderer.concatMatrix(mAnimationMatrix);
}
if (mMatrixFlags != 0) {
if (mMatrixFlags == TRANSLATION) {
- DISPLAY_LIST_LOGD("%s%s %f, %f", indent, "Translate", mTranslationX, mTranslationY);
renderer.translate(mTranslationX, mTranslationY);
} else {
- DISPLAY_LIST_LOGD(
- "%s%s %p: [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f]",
- indent, "ConcatMatrix", mTransformMatrix,
- mTransformMatrix->get(0), mTransformMatrix->get(1),
- mTransformMatrix->get(2), mTransformMatrix->get(3),
- mTransformMatrix->get(4), mTransformMatrix->get(5),
- mTransformMatrix->get(6), mTransformMatrix->get(7),
- mTransformMatrix->get(8));
renderer.concatMatrix(mTransformMatrix);
}
}
if (mAlpha < 1 && !mCaching) {
if (!mHasOverlappingRendering) {
- DISPLAY_LIST_LOGD("%s%s %.2f", indent, "SetAlpha", mAlpha);
renderer.setAlpha(mAlpha);
} else {
// TODO: should be able to store the size of a DL at record time and not
@@ -861,53 +387,35 @@
if (mClipChildren) {
flags |= SkCanvas::kClipToLayer_SaveFlag;
}
- DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %d, 0x%x", indent, "SaveLayerAlpha",
- (float) 0, (float) 0, (float) mRight - mLeft, (float) mBottom - mTop,
- mMultipliedAlpha, flags);
renderer.saveLayerAlpha(0, 0, mRight - mLeft, mBottom - mTop,
mMultipliedAlpha, flags);
}
}
if (mClipChildren) {
- DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f", indent, "ClipRect", 0.0f, 0.0f,
- (float) mRight - mLeft, (float) mBottom - mTop);
renderer.clipRect(0, 0, mRight - mLeft, mBottom - mTop,
SkRegion::kIntersect_Op);
}
}
-/**
- * Changes to replay(), specifically those involving opcode or parameter changes, should be mimicked
- * in the output() function, since that function processes the same list of opcodes for the
- * purposes of logging display list info for a given view.
- */
status_t DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level) {
status_t drawGlStatus = DrawGlInfo::kStatusDone;
- TextContainer text;
- mReader.rewind();
#if DEBUG_DISPLAY_LIST
- uint32_t count = (level + 1) * 2;
- char indent[count + 1];
- for (uint32_t i = 0; i < count; i++) {
- indent[i] = ' ';
- }
- indent[count] = '\0';
Rect* clipRect = renderer.getClipRect();
- DISPLAY_LIST_LOGD("%sStart display list (%p, %s), clipRect: %.0f, %.f, %.0f, %.0f",
- (char*) indent + 2, this, mName.string(), clipRect->left, clipRect->top,
+ DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.f, %.0f, %.0f",
+ (level+1)*2, "", this, mName.string(), clipRect->left, clipRect->top,
clipRect->right, clipRect->bottom);
#endif
renderer.startMark(mName.string());
int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
- DISPLAY_LIST_LOGD("%s%s %d %d", indent, "Save",
+ DISPLAY_LIST_LOGD("%*sSave %d %d", level * 2, "",
SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
setViewProperties(renderer, level);
if (renderer.quickRejectNoScissor(0, 0, mWidth, mHeight)) {
- DISPLAY_LIST_LOGD("%s%s %d", (char*) indent, "RestoreToCount", restoreTo);
+ DISPLAY_LIST_LOGD("%*sRestoreToCount %d", level * 2, "", restoreTo);
renderer.restoreToCount(restoreTo);
renderer.endMark();
return drawGlStatus;
@@ -915,469 +423,22 @@
DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
int saveCount = renderer.getSaveCount() - 1;
-
- while (!mReader.eof()) {
- int op = mReader.readInt();
- if (op & OP_MAY_BE_SKIPPED_MASK) {
- int32_t skip = mReader.readInt();
- if (CC_LIKELY(flags & kReplayFlag_ClipChildren)) {
- mReader.skip(skip);
- DISPLAY_LIST_LOGD("%s%s skipping %d bytes", (char*) indent,
- OP_NAMES[op & ~OP_MAY_BE_SKIPPED_MASK], skip);
- continue;
- } else {
- op &= ~OP_MAY_BE_SKIPPED_MASK;
- }
- }
- logBuffer.writeCommand(level, op);
-
+ for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
+ DisplayListOp *op = mDisplayListData->displayListOps[i];
#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
- Caches::getInstance().eventMark(strlen(OP_NAMES[op]), OP_NAMES[op]);
+ Caches::getInstance().eventMark(strlen(op->name()), op->name());
#endif
- switch (op) {
- case DrawGLFunction: {
- Functor *functor = (Functor *) getInt();
- DISPLAY_LIST_LOGD("%s%s %p", (char*) indent, OP_NAMES[op], functor);
- renderer.startMark("GL functor");
- drawGlStatus |= renderer.callDrawGLFunction(functor, dirty);
- renderer.endMark();
- }
- break;
- case Save: {
- int32_t rendererNum = getInt();
- DISPLAY_LIST_LOGD("%s%s %d", (char*) indent, OP_NAMES[op], rendererNum);
- renderer.save(rendererNum);
- }
- break;
- case Restore: {
- DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
- renderer.restore();
- }
- break;
- case RestoreToCount: {
- int32_t restoreCount = saveCount + getInt();
- DISPLAY_LIST_LOGD("%s%s %d", (char*) indent, OP_NAMES[op], restoreCount);
- renderer.restoreToCount(restoreCount);
- }
- break;
- case SaveLayer: {
- float f1 = getFloat();
- float f2 = getFloat();
- float f3 = getFloat();
- float f4 = getFloat();
- SkPaint* paint = getPaint(renderer);
- int32_t flags = getInt();
- DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %p, 0x%x", (char*) indent,
- OP_NAMES[op], f1, f2, f3, f4, paint, flags);
- renderer.saveLayer(f1, f2, f3, f4, paint, flags);
- }
- break;
- case SaveLayerAlpha: {
- float f1 = getFloat();
- float f2 = getFloat();
- float f3 = getFloat();
- float f4 = getFloat();
- int32_t alpha = getInt();
- int32_t flags = getInt();
- DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %d, 0x%x", (char*) indent,
- OP_NAMES[op], f1, f2, f3, f4, alpha, flags);
- renderer.saveLayerAlpha(f1, f2, f3, f4, alpha, flags);
- }
- break;
- case Translate: {
- float f1 = getFloat();
- float f2 = getFloat();
- DISPLAY_LIST_LOGD("%s%s %.2f, %.2f", (char*) indent, OP_NAMES[op], f1, f2);
- renderer.translate(f1, f2);
- }
- break;
- case Rotate: {
- float rotation = getFloat();
- DISPLAY_LIST_LOGD("%s%s %.2f", (char*) indent, OP_NAMES[op], rotation);
- renderer.rotate(rotation);
- }
- break;
- case Scale: {
- float sx = getFloat();
- float sy = getFloat();
- DISPLAY_LIST_LOGD("%s%s %.2f, %.2f", (char*) indent, OP_NAMES[op], sx, sy);
- renderer.scale(sx, sy);
- }
- break;
- case Skew: {
- float sx = getFloat();
- float sy = getFloat();
- DISPLAY_LIST_LOGD("%s%s %.2f, %.2f", (char*) indent, OP_NAMES[op], sx, sy);
- renderer.skew(sx, sy);
- }
- break;
- case SetMatrix: {
- SkMatrix* matrix = getMatrix();
- DISPLAY_LIST_LOGD("%s%s %p", (char*) indent, OP_NAMES[op], matrix);
- renderer.setMatrix(matrix);
- }
- break;
- case ConcatMatrix: {
- SkMatrix* matrix = getMatrix();
- DISPLAY_LIST_LOGD(
- "%s%s %p: [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f] [%.2f, %.2f, %.2f]",
- (char*) indent, OP_NAMES[op], matrix,
- matrix->get(0), matrix->get(1), matrix->get(2),
- matrix->get(3), matrix->get(4), matrix->get(5),
- matrix->get(6), matrix->get(7), matrix->get(8));
- renderer.concatMatrix(matrix);
- }
- break;
- case ClipRect: {
- float f1 = getFloat();
- float f2 = getFloat();
- float f3 = getFloat();
- float f4 = getFloat();
- int32_t regionOp = getInt();
- DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %d", (char*) indent, OP_NAMES[op],
- f1, f2, f3, f4, regionOp);
- renderer.clipRect(f1, f2, f3, f4, (SkRegion::Op) regionOp);
- }
- break;
- case ClipPath: {
- SkPath* path = getPath();
- int32_t regionOp = getInt();
- DISPLAY_LIST_LOGD("%s%s %d", (char*) indent, OP_NAMES[op], regionOp);
- renderer.clipPath(path, (SkRegion::Op) regionOp);
- }
- break;
- case ClipRegion: {
- SkRegion* region = getRegion();
- int32_t regionOp = getInt();
- DISPLAY_LIST_LOGD("%s%s %d", (char*) indent, OP_NAMES[op], regionOp);
- renderer.clipRegion(region, (SkRegion::Op) regionOp);
- }
- break;
- case DrawDisplayList: {
- DisplayList* displayList = getDisplayList();
- int32_t flags = getInt();
- DISPLAY_LIST_LOGD("%s%s %p, %dx%d, 0x%x %d", (char*) indent, OP_NAMES[op],
- displayList, mWidth, mHeight, flags, level + 1);
- drawGlStatus |= renderer.drawDisplayList(displayList, dirty, flags, level + 1);
- }
- break;
- case DrawLayer: {
- int oldAlpha = -1;
- Layer* layer = (Layer*) getInt();
- float x = getFloat();
- float y = getFloat();
- SkPaint* paint = getPaint(renderer);
- if (mCaching && mMultipliedAlpha < 255) {
- oldAlpha = layer->getAlpha();
- layer->setAlpha(mMultipliedAlpha);
- }
- DISPLAY_LIST_LOGD("%s%s %p, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op],
- layer, x, y, paint);
- drawGlStatus |= renderer.drawLayer(layer, x, y, paint);
- if (oldAlpha >= 0) {
- layer->setAlpha(oldAlpha);
- }
- }
- break;
- case DrawBitmap: {
- int oldAlpha = -1;
- SkBitmap* bitmap = getBitmap();
- float x = getFloat();
- float y = getFloat();
- SkPaint* paint = getPaint(renderer);
- if (mCaching && mMultipliedAlpha < 255) {
- oldAlpha = paint->getAlpha();
- paint->setAlpha(mMultipliedAlpha);
- }
- DISPLAY_LIST_LOGD("%s%s %p, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op],
- bitmap, x, y, paint);
- drawGlStatus |= renderer.drawBitmap(bitmap, x, y, paint);
- if (oldAlpha >= 0) {
- paint->setAlpha(oldAlpha);
- }
- }
- break;
- case DrawBitmapMatrix: {
- SkBitmap* bitmap = getBitmap();
- SkMatrix* matrix = getMatrix();
- SkPaint* paint = getPaint(renderer);
- DISPLAY_LIST_LOGD("%s%s %p, %p, %p", (char*) indent, OP_NAMES[op],
- bitmap, matrix, paint);
- drawGlStatus |= renderer.drawBitmap(bitmap, matrix, paint);
- }
- break;
- case DrawBitmapRect: {
- SkBitmap* bitmap = getBitmap();
- float f1 = getFloat();
- float f2 = getFloat();
- float f3 = getFloat();
- float f4 = getFloat();
- float f5 = getFloat();
- float f6 = getFloat();
- float f7 = getFloat();
- float f8 = getFloat();
- SkPaint* paint = getPaint(renderer);
- DISPLAY_LIST_LOGD("%s%s %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %p",
- (char*) indent, OP_NAMES[op], bitmap,
- f1, f2, f3, f4, f5, f6, f7, f8,paint);
- drawGlStatus |= renderer.drawBitmap(bitmap, f1, f2, f3, f4, f5, f6, f7, f8, paint);
- }
- break;
- case DrawBitmapData: {
- SkBitmap* bitmap = getBitmapData();
- float x = getFloat();
- float y = getFloat();
- SkPaint* paint = getPaint(renderer);
- DISPLAY_LIST_LOGD("%s%s %p, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op],
- bitmap, x, y, paint);
- drawGlStatus |= renderer.drawBitmap(bitmap, x, y, paint);
- }
- break;
- case DrawBitmapMesh: {
- int32_t verticesCount = 0;
- uint32_t colorsCount = 0;
-
- SkBitmap* bitmap = getBitmap();
- uint32_t meshWidth = getInt();
- uint32_t meshHeight = getInt();
- float* vertices = getFloats(verticesCount);
- bool hasColors = getInt();
- int32_t* colors = hasColors ? getInts(colorsCount) : NULL;
- SkPaint* paint = getPaint(renderer);
-
- DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
- drawGlStatus |= renderer.drawBitmapMesh(bitmap, meshWidth, meshHeight, vertices,
- colors, paint);
- }
- break;
- case DrawPatch: {
- int32_t* xDivs = NULL;
- int32_t* yDivs = NULL;
- uint32_t* colors = NULL;
- uint32_t xDivsCount = 0;
- uint32_t yDivsCount = 0;
- int8_t numColors = 0;
-
- SkBitmap* bitmap = getBitmap();
-
- xDivs = getInts(xDivsCount);
- yDivs = getInts(yDivsCount);
- colors = getUInts(numColors);
-
- float left = getFloat();
- float top = getFloat();
- float right = getFloat();
- float bottom = getFloat();
-
- int alpha = getInt();
- SkXfermode::Mode mode = (SkXfermode::Mode) getInt();
-
- DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
- drawGlStatus |= renderer.drawPatch(bitmap, xDivs, yDivs, colors,
- xDivsCount, yDivsCount, numColors, left, top, right, bottom,
- alpha, mode);
- }
- break;
- case DrawColor: {
- int32_t color = getInt();
- int32_t xferMode = getInt();
- DISPLAY_LIST_LOGD("%s%s 0x%x %d", (char*) indent, OP_NAMES[op], color, xferMode);
- drawGlStatus |= renderer.drawColor(color, (SkXfermode::Mode) xferMode);
- }
- break;
- case DrawRect: {
- float f1 = getFloat();
- float f2 = getFloat();
- float f3 = getFloat();
- float f4 = getFloat();
- SkPaint* paint = getPaint(renderer);
- DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op],
- f1, f2, f3, f4, paint);
- drawGlStatus |= renderer.drawRect(f1, f2, f3, f4, paint);
- }
- break;
- case DrawRoundRect: {
- float f1 = getFloat();
- float f2 = getFloat();
- float f3 = getFloat();
- float f4 = getFloat();
- float f5 = getFloat();
- float f6 = getFloat();
- SkPaint* paint = getPaint(renderer);
- DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %p",
- (char*) indent, OP_NAMES[op], f1, f2, f3, f4, f5, f6, paint);
- drawGlStatus |= renderer.drawRoundRect(f1, f2, f3, f4, f5, f6, paint);
- }
- break;
- case DrawCircle: {
- float f1 = getFloat();
- float f2 = getFloat();
- float f3 = getFloat();
- SkPaint* paint = getPaint(renderer);
- DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %p",
- (char*) indent, OP_NAMES[op], f1, f2, f3, paint);
- drawGlStatus |= renderer.drawCircle(f1, f2, f3, paint);
- }
- break;
- case DrawOval: {
- float f1 = getFloat();
- float f2 = getFloat();
- float f3 = getFloat();
- float f4 = getFloat();
- SkPaint* paint = getPaint(renderer);
- DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %p",
- (char*) indent, OP_NAMES[op], f1, f2, f3, f4, paint);
- drawGlStatus |= renderer.drawOval(f1, f2, f3, f4, paint);
- }
- break;
- case DrawArc: {
- float f1 = getFloat();
- float f2 = getFloat();
- float f3 = getFloat();
- float f4 = getFloat();
- float f5 = getFloat();
- float f6 = getFloat();
- int32_t i1 = getInt();
- SkPaint* paint = getPaint(renderer);
- DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p",
- (char*) indent, OP_NAMES[op], f1, f2, f3, f4, f5, f6, i1, paint);
- drawGlStatus |= renderer.drawArc(f1, f2, f3, f4, f5, f6, i1 == 1, paint);
- }
- break;
- case DrawPath: {
- SkPath* path = getPath();
- SkPaint* paint = getPaint(renderer);
- DISPLAY_LIST_LOGD("%s%s %p, %p", (char*) indent, OP_NAMES[op], path, paint);
- drawGlStatus |= renderer.drawPath(path, paint);
- }
- break;
- case DrawLines: {
- int32_t count = 0;
- float* points = getFloats(count);
- SkPaint* paint = getPaint(renderer);
- DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
- drawGlStatus |= renderer.drawLines(points, count, paint);
- }
- break;
- case DrawPoints: {
- int32_t count = 0;
- float* points = getFloats(count);
- SkPaint* paint = getPaint(renderer);
- DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
- drawGlStatus |= renderer.drawPoints(points, count, paint);
- }
- break;
- case DrawTextOnPath: {
- getText(&text);
- int32_t count = getInt();
- SkPath* path = getPath();
- float hOffset = getFloat();
- float vOffset = getFloat();
- SkPaint* paint = getPaint(renderer);
- DISPLAY_LIST_LOGD("%s%s %s, %d, %d, %p", (char*) indent, OP_NAMES[op],
- text.text(), text.length(), count, paint);
- drawGlStatus |= renderer.drawTextOnPath(text.text(), text.length(), count, path,
- hOffset, vOffset, paint);
- }
- break;
- case DrawPosText: {
- getText(&text);
- int32_t count = getInt();
- int32_t positionsCount = 0;
- float* positions = getFloats(positionsCount);
- SkPaint* paint = getPaint(renderer);
- DISPLAY_LIST_LOGD("%s%s %s, %d, %d, %p", (char*) indent,
- OP_NAMES[op], text.text(), text.length(), count, paint);
- drawGlStatus |= renderer.drawPosText(text.text(), text.length(), count,
- positions, paint);
- }
- break;
- case DrawText: {
- getText(&text);
- int32_t count = getInt();
- float x = getFloat();
- float y = getFloat();
- int32_t positionsCount = 0;
- float* positions = getFloats(positionsCount);
- SkPaint* paint = getPaint(renderer);
- float length = getFloat();
- DISPLAY_LIST_LOGD("%s%s %s, %d, %d, %.2f, %.2f, %p, %.2f", (char*) indent,
- OP_NAMES[op], text.text(), text.length(), count, x, y, paint, length);
- drawGlStatus |= renderer.drawText(text.text(), text.length(), count,
- x, y, positions, paint, length);
- }
- break;
- case DrawRects: {
- int32_t count = 0;
- float* rects = getFloats(count);
- SkPaint* paint = getPaint(renderer);
- DISPLAY_LIST_LOGD("%s%s %d, %p", (char*) indent, OP_NAMES[op], count, paint);
- drawGlStatus |= renderer.drawRects(rects, count / 4, paint);
- }
- break;
- case ResetShader: {
- DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
- renderer.resetShader();
- }
- break;
- case SetupShader: {
- SkiaShader* shader = getShader();
- DISPLAY_LIST_LOGD("%s%s %p", (char*) indent, OP_NAMES[op], shader);
- renderer.setupShader(shader);
- }
- break;
- case ResetColorFilter: {
- DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
- renderer.resetColorFilter();
- }
- break;
- case SetupColorFilter: {
- SkiaColorFilter *colorFilter = getColorFilter();
- DISPLAY_LIST_LOGD("%s%s %p", (char*) indent, OP_NAMES[op], colorFilter);
- renderer.setupColorFilter(colorFilter);
- }
- break;
- case ResetShadow: {
- DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
- renderer.resetShadow();
- }
- break;
- case SetupShadow: {
- float radius = getFloat();
- float dx = getFloat();
- float dy = getFloat();
- int32_t color = getInt();
- DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, 0x%x", (char*) indent, OP_NAMES[op],
- radius, dx, dy, color);
- renderer.setupShadow(radius, dx, dy, color);
- }
- break;
- case ResetPaintFilter: {
- DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
- renderer.resetPaintFilter();
- }
- break;
- case SetupPaintFilter: {
- int32_t clearBits = getInt();
- int32_t setBits = getInt();
- DISPLAY_LIST_LOGD("%s%s 0x%x, 0x%x", (char*) indent, OP_NAMES[op],
- clearBits, setBits);
- renderer.setupPaintFilter(clearBits, setBits);
- }
- break;
- default:
- DISPLAY_LIST_LOGD("Display List error: op not handled: %s%s",
- (char*) indent, OP_NAMES[op]);
- break;
- }
+ drawGlStatus |= op->replay(renderer, dirty, flags,
+ saveCount, level, mCaching, mMultipliedAlpha);
+ logBuffer.writeCommand(level, op->name());
}
- DISPLAY_LIST_LOGD("%s%s %d", (char*) indent, "RestoreToCount", restoreTo);
+ DISPLAY_LIST_LOGD("%*sRestoreToCount %d", level * 2, "", restoreTo);
renderer.restoreToCount(restoreTo);
renderer.endMark();
- DISPLAY_LIST_LOGD("%sDone (%p, %s), returning %d", (char*) indent + 2, this, mName.string(),
+ DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", (level + 1) * 2, "", this, mName.string(),
drawGlStatus);
return drawGlStatus;
}
@@ -1387,7 +448,7 @@
///////////////////////////////////////////////////////////////////////////////
DisplayListRenderer::DisplayListRenderer():
- mCaches(Caches::getInstance()), mWriter(MIN_WRITER_SIZE),
+ mCaches(Caches::getInstance()), mDisplayListData(new DisplayListData),
mTranslateX(0.0f), mTranslateY(0.0f), mHasTranslate(false),
mHasDrawOps(false), mFunctorCount(0) {
}
@@ -1397,8 +458,7 @@
}
void DisplayListRenderer::reset() {
- mWriter.reset();
-
+ mDisplayListData = new DisplayListData();
mCaches.resourceCache.lock();
for (size_t i = 0; i < mBitmapResources.size(); i++) {
@@ -1493,7 +553,7 @@
void DisplayListRenderer::finish() {
insertRestoreToCount();
- insertTranlate();
+ insertTranslate();
}
void DisplayListRenderer::interrupt() {
@@ -1504,15 +564,13 @@
status_t DisplayListRenderer::callDrawGLFunction(Functor *functor, Rect& dirty) {
// Ignore dirty during recording, it matters only when we replay
- addOp(DisplayList::DrawGLFunction);
- addInt((int) functor);
+ addDrawOp(new (alloc()) DrawFunctorOp(functor));
mFunctorCount++;
return DrawGlInfo::kStatusDone; // No invalidate needed at record-time
}
int DisplayListRenderer::save(int flags) {
- addOp(DisplayList::Save);
- addInt(flags);
+ addStateOp(new (alloc()) SaveOp(flags));
return OpenGLRenderer::save(flags);
}
@@ -1523,31 +581,25 @@
}
mRestoreSaveCount--;
- insertTranlate();
+ insertTranslate();
OpenGLRenderer::restore();
}
void DisplayListRenderer::restoreToCount(int saveCount) {
mRestoreSaveCount = saveCount;
- insertTranlate();
+ insertTranslate();
OpenGLRenderer::restoreToCount(saveCount);
}
int DisplayListRenderer::saveLayer(float left, float top, float right, float bottom,
SkPaint* p, int flags) {
- addOp(DisplayList::SaveLayer);
- addBounds(left, top, right, bottom);
- addPaint(p);
- addInt(flags);
+ addStateOp(new (alloc()) SaveLayerOp(left, top, right, bottom, p, flags));
return OpenGLRenderer::save(flags);
}
int DisplayListRenderer::saveLayerAlpha(float left, float top, float right, float bottom,
int alpha, int flags) {
- addOp(DisplayList::SaveLayerAlpha);
- addBounds(left, top, right, bottom);
- addInt(alpha);
- addInt(flags);
+ addStateOp(new (alloc()) SaveLayerAlphaOp(left, top, right, bottom, alpha, flags));
return OpenGLRenderer::save(flags);
}
@@ -1560,54 +612,47 @@
}
void DisplayListRenderer::rotate(float degrees) {
- addOp(DisplayList::Rotate);
- addFloat(degrees);
+ addStateOp(new (alloc()) RotateOp(degrees));
OpenGLRenderer::rotate(degrees);
}
void DisplayListRenderer::scale(float sx, float sy) {
- addOp(DisplayList::Scale);
- addPoint(sx, sy);
+ addStateOp(new (alloc()) ScaleOp(sx, sy));
OpenGLRenderer::scale(sx, sy);
}
void DisplayListRenderer::skew(float sx, float sy) {
- addOp(DisplayList::Skew);
- addPoint(sx, sy);
+ addStateOp(new (alloc()) SkewOp(sx, sy));
OpenGLRenderer::skew(sx, sy);
}
void DisplayListRenderer::setMatrix(SkMatrix* matrix) {
- addOp(DisplayList::SetMatrix);
- addMatrix(matrix);
+ matrix = refMatrix(matrix);
+ addStateOp(new (alloc()) SetMatrixOp(matrix));
OpenGLRenderer::setMatrix(matrix);
}
void DisplayListRenderer::concatMatrix(SkMatrix* matrix) {
- addOp(DisplayList::ConcatMatrix);
- addMatrix(matrix);
+ matrix = refMatrix(matrix);
+ addStateOp(new (alloc()) ConcatMatrixOp(matrix));
OpenGLRenderer::concatMatrix(matrix);
}
bool DisplayListRenderer::clipRect(float left, float top, float right, float bottom,
SkRegion::Op op) {
- addOp(DisplayList::ClipRect);
- addBounds(left, top, right, bottom);
- addInt(op);
+ addStateOp(new (alloc()) ClipRectOp(left, top, right, bottom, op));
return OpenGLRenderer::clipRect(left, top, right, bottom, op);
}
bool DisplayListRenderer::clipPath(SkPath* path, SkRegion::Op op) {
- addOp(DisplayList::ClipPath);
- addPath(path);
- addInt(op);
+ path = refPath(path);
+ addStateOp(new (alloc()) ClipPathOp(path, op));
return OpenGLRenderer::clipPath(path, op);
}
bool DisplayListRenderer::clipRegion(SkRegion* region, SkRegion::Op op) {
- addOp(DisplayList::ClipRegion);
- addRegion(region);
- addInt(op);
+ region = refRegion(region);
+ addStateOp(new (alloc()) ClipRegionOp(region, op));
return OpenGLRenderer::clipRegion(region, op);
}
@@ -1616,84 +661,71 @@
// dirty is an out parameter and should not be recorded,
// it matters only when replaying the display list
- addOp(DisplayList::DrawDisplayList);
- addDisplayList(displayList);
- addInt(flags);
+ // TODO: To be safe, the display list should be ref-counted in the
+ // resources cache, but we rely on the caller (UI toolkit) to
+ // do the right thing for now
+
+ addDrawOp(new (alloc()) DrawDisplayListOp(displayList, flags));
return DrawGlInfo::kStatusDone;
}
status_t DisplayListRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* paint) {
- addOp(DisplayList::DrawLayer);
- addLayer(layer);
- addPoint(x, y);
- addPaint(paint);
+ mLayers.add(layer);
+ mCaches.resourceCache.incrementRefcount(layer);
+ paint = refPaint(paint);
+
+ addDrawOp(new (alloc()) DrawLayerOp(layer, x, y, paint));
return DrawGlInfo::kStatusDone;
}
status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint) {
- const bool reject = quickRejectNoScissor(left, top,
- left + bitmap->width(), top + bitmap->height());
- uint32_t* location = addOp(DisplayList::DrawBitmap, reject);
- addBitmap(bitmap);
- addPoint(left, top);
- addPaint(paint);
- addSkip(location);
+ bitmap = refBitmap(bitmap);
+ paint = refPaint(paint);
+
+ addDrawOp(new (alloc()) DrawBitmapOp(bitmap, left, top, paint));
return DrawGlInfo::kStatusDone;
}
status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint) {
- Rect r(0.0f, 0.0f, bitmap->width(), bitmap->height());
- const mat4 transform(*matrix);
- transform.mapRect(r);
+ bitmap = refBitmap(bitmap);
+ matrix = refMatrix(matrix);
+ paint = refPaint(paint);
- const bool reject = quickRejectNoScissor(r.left, r.top, r.right, r.bottom);
- uint32_t* location = addOp(DisplayList::DrawBitmapMatrix, reject);
- addBitmap(bitmap);
- addMatrix(matrix);
- addPaint(paint);
- addSkip(location);
+ addDrawOp(new (alloc()) DrawBitmapMatrixOp(bitmap, matrix, paint));
return DrawGlInfo::kStatusDone;
}
status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
float dstRight, float dstBottom, SkPaint* paint) {
- const bool reject = quickRejectNoScissor(dstLeft, dstTop, dstRight, dstBottom);
- uint32_t* location = addOp(DisplayList::DrawBitmapRect, reject);
- addBitmap(bitmap);
- addBounds(srcLeft, srcTop, srcRight, srcBottom);
- addBounds(dstLeft, dstTop, dstRight, dstBottom);
- addPaint(paint);
- addSkip(location);
+ bitmap = refBitmap(bitmap);
+ paint = refPaint(paint);
+
+ addDrawOp(new (alloc()) DrawBitmapRectOp(bitmap,
+ srcLeft, srcTop, srcRight, srcBottom,
+ dstLeft, dstTop, dstRight, dstBottom, paint));
return DrawGlInfo::kStatusDone;
}
status_t DisplayListRenderer::drawBitmapData(SkBitmap* bitmap, float left, float top,
SkPaint* paint) {
- const bool reject = quickRejectNoScissor(left, top,
- left + bitmap->width(), top + bitmap->height());
- uint32_t* location = addOp(DisplayList::DrawBitmapData, reject);
- addBitmapData(bitmap);
- addPoint(left, top);
- addPaint(paint);
- addSkip(location);
+ bitmap = refBitmapData(bitmap);
+ paint = refPaint(paint);
+
+ addDrawOp(new (alloc()) DrawBitmapDataOp(bitmap, left, top, paint));
return DrawGlInfo::kStatusDone;
}
status_t DisplayListRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight,
float* vertices, int* colors, SkPaint* paint) {
- addOp(DisplayList::DrawBitmapMesh);
- addBitmap(bitmap);
- addInt(meshWidth);
- addInt(meshHeight);
- addFloats(vertices, (meshWidth + 1) * (meshHeight + 1) * 2);
- if (colors) {
- addInt(1);
- addInts(colors, (meshWidth + 1) * (meshHeight + 1));
- } else {
- addInt(0);
- }
- addPaint(paint);
+ int count = (meshWidth + 1) * (meshHeight + 1) * 2;
+ bitmap = refBitmap(bitmap);
+ vertices = refBuffer<float>(vertices, count);
+ paint = refPaint(paint);
+ colors = refBuffer<int>(colors, count);
+
+ addDrawOp(new (alloc()) DrawBitmapMeshOp(bitmap, meshWidth, meshHeight,
+ vertices, colors, paint));
return DrawGlInfo::kStatusDone;
}
@@ -1704,132 +736,114 @@
SkXfermode::Mode mode;
OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode);
- const bool reject = quickRejectNoScissor(left, top, right, bottom);
- uint32_t* location = addOp(DisplayList::DrawPatch, reject);
- addBitmap(bitmap);
- addInts(xDivs, width);
- addInts(yDivs, height);
- addUInts(colors, numColors);
- addBounds(left, top, right, bottom);
- addInt(alpha);
- addInt(mode);
- addSkip(location);
+ bitmap = refBitmap(bitmap);
+ xDivs = refBuffer<int>(xDivs, width);
+ yDivs = refBuffer<int>(yDivs, height);
+ colors = refBuffer<uint32_t>(colors, numColors);
+
+ addDrawOp(new (alloc()) DrawPatchOp(bitmap, xDivs, yDivs, colors, width, height, numColors,
+ left, top, right, bottom, alpha, mode));
return DrawGlInfo::kStatusDone;
}
status_t DisplayListRenderer::drawColor(int color, SkXfermode::Mode mode) {
- addOp(DisplayList::DrawColor);
- addInt(color);
- addInt(mode);
+ addDrawOp(new (alloc()) DrawColorOp(color, mode));
return DrawGlInfo::kStatusDone;
}
status_t DisplayListRenderer::drawRect(float left, float top, float right, float bottom,
SkPaint* paint) {
- const bool reject = paint->getStyle() == SkPaint::kFill_Style &&
- quickRejectNoScissor(left, top, right, bottom);
- uint32_t* location = addOp(DisplayList::DrawRect, reject);
- addBounds(left, top, right, bottom);
- addPaint(paint);
- addSkip(location);
+ paint = refPaint(paint);
+ addDrawOp(new (alloc()) DrawRectOp(left, top, right, bottom, paint));
return DrawGlInfo::kStatusDone;
}
status_t DisplayListRenderer::drawRoundRect(float left, float top, float right, float bottom,
float rx, float ry, SkPaint* paint) {
- const bool reject = paint->getStyle() == SkPaint::kFill_Style &&
- quickRejectNoScissor(left, top, right, bottom);
- uint32_t* location = addOp(DisplayList::DrawRoundRect, reject);
- addBounds(left, top, right, bottom);
- addPoint(rx, ry);
- addPaint(paint);
- addSkip(location);
+ paint = refPaint(paint);
+ addDrawOp(new (alloc()) DrawRoundRectOp(left, top, right, bottom, rx, ry, paint));
return DrawGlInfo::kStatusDone;
}
status_t DisplayListRenderer::drawCircle(float x, float y, float radius, SkPaint* paint) {
- addOp(DisplayList::DrawCircle);
- addPoint(x, y);
- addFloat(radius);
- addPaint(paint);
+ paint = refPaint(paint);
+ addDrawOp(new (alloc()) DrawCircleOp(x, y, radius, paint));
return DrawGlInfo::kStatusDone;
}
status_t DisplayListRenderer::drawOval(float left, float top, float right, float bottom,
SkPaint* paint) {
- addOp(DisplayList::DrawOval);
- addBounds(left, top, right, bottom);
- addPaint(paint);
+ paint = refPaint(paint);
+ addDrawOp(new (alloc()) DrawOvalOp(left, top, right, bottom, paint));
return DrawGlInfo::kStatusDone;
}
status_t DisplayListRenderer::drawArc(float left, float top, float right, float bottom,
float startAngle, float sweepAngle, bool useCenter, SkPaint* paint) {
- addOp(DisplayList::DrawArc);
- addBounds(left, top, right, bottom);
- addPoint(startAngle, sweepAngle);
- addInt(useCenter ? 1 : 0);
- addPaint(paint);
+ paint = refPaint(paint);
+ addDrawOp(new (alloc()) DrawArcOp(left, top, right, bottom,
+ startAngle, sweepAngle, useCenter, paint));
return DrawGlInfo::kStatusDone;
}
status_t DisplayListRenderer::drawPath(SkPath* path, SkPaint* paint) {
- float left, top, offset;
- uint32_t width, height;
- computePathBounds(path, paint, left, top, offset, width, height);
+ path = refPath(path);
+ paint = refPaint(paint);
- left -= offset;
- top -= offset;
-
- const bool reject = quickRejectNoScissor(left, top, left + width, top + height);
- uint32_t* location = addOp(DisplayList::DrawPath, reject);
- addPath(path);
- addPaint(paint);
- addSkip(location);
+ addDrawOp(new (alloc()) DrawPathOp(path, paint));
return DrawGlInfo::kStatusDone;
}
status_t DisplayListRenderer::drawLines(float* points, int count, SkPaint* paint) {
- addOp(DisplayList::DrawLines);
- addFloats(points, count);
- addPaint(paint);
+ points = refBuffer<float>(points, count);
+ paint = refPaint(paint);
+
+ addDrawOp(new (alloc()) DrawLinesOp(points, count, paint));
return DrawGlInfo::kStatusDone;
}
status_t DisplayListRenderer::drawPoints(float* points, int count, SkPaint* paint) {
- addOp(DisplayList::DrawPoints);
- addFloats(points, count);
- addPaint(paint);
+ points = refBuffer<float>(points, count);
+ paint = refPaint(paint);
+
+ addDrawOp(new (alloc()) DrawPointsOp(points, count, paint));
return DrawGlInfo::kStatusDone;
}
status_t DisplayListRenderer::drawTextOnPath(const char* text, int bytesCount, int count,
SkPath* path, float hOffset, float vOffset, SkPaint* paint) {
if (!text || count <= 0) return DrawGlInfo::kStatusDone;
- addOp(DisplayList::DrawTextOnPath);
- addText(text, bytesCount);
- addInt(count);
- addPath(path);
- addFloat(hOffset);
- addFloat(vOffset);
+
paint->setAntiAlias(true);
- SkPaint* addedPaint = addPaint(paint);
- FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(addedPaint);
- fontRenderer.precache(addedPaint, text, count, *mSnapshot->transform);
+ text = refText(text, bytesCount);
+ path = refPath(path);
+ paint = refPaint(paint);
+
+ DrawOp* op = new (alloc()) DrawTextOnPathOp(text, bytesCount, count, path,
+ hOffset, vOffset, paint);
+ if (addDrawOp(op)) {
+ // precache if draw operation is visible
+ FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
+ fontRenderer.precache(paint, text, count, *mSnapshot->transform);
+ }
return DrawGlInfo::kStatusDone;
}
status_t DisplayListRenderer::drawPosText(const char* text, int bytesCount, int count,
const float* positions, SkPaint* paint) {
if (!text || count <= 0) return DrawGlInfo::kStatusDone;
- addOp(DisplayList::DrawPosText);
- addText(text, bytesCount);
- addInt(count);
- addFloats(positions, count * 2);
+
paint->setAntiAlias(true);
- SkPaint* addedPaint = addPaint(paint);
- FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(addedPaint);
- fontRenderer.precache(addedPaint, text, count, *mSnapshot->transform);
+ text = refText(text, bytesCount);
+ positions = refBuffer<float>(positions, count * 2);
+ paint = refPaint(paint);
+
+ DrawOp* op = new (alloc()) DrawPosTextOp(text, bytesCount, count, positions, paint);
+ if (addDrawOp(op)) {
+ // precache if draw operation is visible
+ FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
+ fontRenderer.precache(paint, text, count, *mSnapshot->transform);
+ }
return DrawGlInfo::kStatusDone;
}
@@ -1847,75 +861,96 @@
paint->setAntiAlias(true);
if (length < 0.0f) length = paint->measureText(text, bytesCount);
- bool reject = false;
- if (CC_LIKELY(paint->getTextAlign() == SkPaint::kLeft_Align)) {
- SkPaint::FontMetrics metrics;
- paint->getFontMetrics(&metrics, 0.0f);
- reject = quickRejectNoScissor(x, y + metrics.fTop, x + length, y + metrics.fBottom);
- }
+ text = refText(text, bytesCount);
+ positions = refBuffer<float>(positions, count * 2);
+ paint = refPaint(paint);
- uint32_t* location = addOp(DisplayList::DrawText, reject);
- addText(text, bytesCount);
- addInt(count);
- addFloat(x);
- addFloat(y);
- addFloats(positions, count * 2);
- SkPaint* addedPaint = addPaint(paint);
- if (!reject) {
- FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(addedPaint);
- fontRenderer.precache(addedPaint, text, count, *mSnapshot->transform);
+ DrawOp* op = new (alloc()) DrawTextOp(text, bytesCount, count, x, y, positions, paint, length);
+ if (addDrawOp(op)) {
+ // precache if draw operation is visible
+ FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
+ fontRenderer.precache(paint, text, count, *mSnapshot->transform);
}
- addFloat(length);
- addSkip(location);
return DrawGlInfo::kStatusDone;
}
status_t DisplayListRenderer::drawRects(const float* rects, int count, SkPaint* paint) {
if (count <= 0) return DrawGlInfo::kStatusDone;
- addOp(DisplayList::DrawRects);
- addFloats(rects, count * 4);
- addPaint(paint);
+ rects = refBuffer<float>(rects, count);
+ paint = refPaint(paint);
+ addDrawOp(new (alloc()) DrawRectsOp(rects, count, paint));
return DrawGlInfo::kStatusDone;
}
void DisplayListRenderer::resetShader() {
- addOp(DisplayList::ResetShader);
+ addStateOp(new (alloc()) ResetShaderOp());
}
void DisplayListRenderer::setupShader(SkiaShader* shader) {
- addOp(DisplayList::SetupShader);
- addShader(shader);
+ shader = refShader(shader);
+ addStateOp(new (alloc()) SetupShaderOp(shader));
}
void DisplayListRenderer::resetColorFilter() {
- addOp(DisplayList::ResetColorFilter);
+ addStateOp(new (alloc()) ResetColorFilterOp());
}
void DisplayListRenderer::setupColorFilter(SkiaColorFilter* filter) {
- addOp(DisplayList::SetupColorFilter);
- addColorFilter(filter);
+ filter = refColorFilter(filter);
+ addStateOp(new (alloc()) SetupColorFilterOp(filter));
}
void DisplayListRenderer::resetShadow() {
- addOp(DisplayList::ResetShadow);
+ addStateOp(new (alloc()) ResetShadowOp());
}
void DisplayListRenderer::setupShadow(float radius, float dx, float dy, int color) {
- addOp(DisplayList::SetupShadow);
- addFloat(radius);
- addPoint(dx, dy);
- addInt(color);
+ addStateOp(new (alloc()) SetupShadowOp(radius, dx, dy, color));
}
void DisplayListRenderer::resetPaintFilter() {
- addOp(DisplayList::ResetPaintFilter);
+ addStateOp(new (alloc()) ResetPaintFilterOp());
}
void DisplayListRenderer::setupPaintFilter(int clearBits, int setBits) {
- addOp(DisplayList::SetupPaintFilter);
- addInt(clearBits);
- addInt(setBits);
+ addStateOp(new (alloc()) SetupPaintFilterOp(clearBits, setBits));
+}
+
+void DisplayListRenderer::insertRestoreToCount() {
+ if (mRestoreSaveCount >= 0) {
+ DisplayListOp* op = new (alloc()) RestoreToCountOp(mRestoreSaveCount);
+ mDisplayListData->displayListOps.add(op);
+ mRestoreSaveCount = -1;
+ }
+}
+
+void DisplayListRenderer::insertTranslate() {
+ if (mHasTranslate) {
+ if (mTranslateX != 0.0f || mTranslateY != 0.0f) {
+ DisplayListOp* op = new (alloc()) TranslateOp(mTranslateX, mTranslateY);
+ mDisplayListData->displayListOps.add(op);
+ mTranslateX = mTranslateY = 0.0f;
+ }
+ mHasTranslate = false;
+ }
+}
+
+void DisplayListRenderer::addStateOp(StateOp* op) {
+ addOpInternal(op);
+}
+
+bool DisplayListRenderer::addDrawOp(DrawOp* op) {
+ bool rejected = false;
+ Rect localBounds;
+ if (op->getLocalBounds(localBounds)) {
+ rejected = quickRejectNoScissor(localBounds.left, localBounds.top,
+ localBounds.right, localBounds.bottom);
+ op->setQuickRejected(rejected);
+ }
+ mHasDrawOps = true;
+ addOpInternal(op);
+ return !rejected;
}
}; // namespace uirenderer
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index f55f1f2..b25288b 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -31,6 +31,7 @@
#include "DisplayListLogBuffer.h"
#include "OpenGLRenderer.h"
+#include "utils/LinearAllocator.h"
namespace android {
namespace uirenderer {
@@ -60,6 +61,18 @@
///////////////////////////////////////////////////////////////////////////////
class DisplayListRenderer;
+class DisplayListOp;
+class DrawOp;
+class StateOp;
+
+/**
+ * Refcounted structure that holds data used in display list stream
+ */
+class DisplayListData: public LightRefBase<DisplayListData> {
+public:
+ LinearAllocator allocator;
+ Vector<DisplayListOp*> displayListOps;
+};
/**
* Replays recorded drawing commands.
@@ -69,66 +82,13 @@
DisplayList(const DisplayListRenderer& recorder);
ANDROID_API ~DisplayList();
- // IMPORTANT: Update the intialization of OP_NAMES in the .cpp file
- // when modifying this file
- enum Op {
- // Non-drawing operations
- Save = 0,
- Restore,
- RestoreToCount,
- SaveLayer,
- SaveLayerAlpha,
- Translate,
- Rotate,
- Scale,
- Skew,
- SetMatrix,
- ConcatMatrix,
- ClipRect,
- ClipPath,
- ClipRegion,
- // Drawing operations
- DrawDisplayList,
- DrawLayer,
- DrawBitmap,
- DrawBitmapMatrix,
- DrawBitmapRect,
- DrawBitmapData,
- DrawBitmapMesh,
- DrawPatch,
- DrawColor,
- DrawRect,
- DrawRoundRect,
- DrawCircle,
- DrawOval,
- DrawArc,
- DrawPath,
- DrawLines,
- DrawPoints,
- DrawTextOnPath,
- DrawPosText,
- DrawText,
- DrawRects,
- ResetShader,
- SetupShader,
- ResetColorFilter,
- SetupColorFilter,
- ResetShadow,
- SetupShadow,
- ResetPaintFilter,
- SetupPaintFilter,
- DrawGLFunction,
- };
-
// See flags defined in DisplayList.java
enum ReplayFlag {
kReplayFlag_ClipChildren = 0x1
};
- static const char* OP_NAMES[];
-
void setViewProperties(OpenGLRenderer& renderer, uint32_t level);
- void outputViewProperties(OpenGLRenderer& renderer, char* indent);
+ void outputViewProperties(uint32_t level);
ANDROID_API size_t getSize();
ANDROID_API static void destroyDisplayListDeferred(DisplayList* displayList);
@@ -138,7 +98,7 @@
status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level = 0);
- void output(OpenGLRenderer& renderer, uint32_t level = 0);
+ void output(uint32_t level = 0);
ANDROID_API void reset();
@@ -423,78 +383,6 @@
const char* mText;
};
- SkBitmap* getBitmap() {
- return (SkBitmap*) getInt();
- }
-
- SkBitmap* getBitmapData() {
- return (SkBitmap*) getInt();
- }
-
- SkiaShader* getShader() {
- return (SkiaShader*) getInt();
- }
-
- SkiaColorFilter* getColorFilter() {
- return (SkiaColorFilter*) getInt();
- }
-
- inline int32_t getIndex() {
- return mReader.readInt();
- }
-
- inline int32_t getInt() {
- return mReader.readInt();
- }
-
- inline uint32_t getUInt() {
- return mReader.readU32();
- }
-
- SkMatrix* getMatrix() {
- return (SkMatrix*) getInt();
- }
-
- SkPath* getPath() {
- return (SkPath*) getInt();
- }
-
- SkRegion* getRegion() {
- return (SkRegion*) getInt();
- }
-
- SkPaint* getPaint(OpenGLRenderer& renderer) {
- return renderer.filterPaint((SkPaint*) getInt());
- }
-
- DisplayList* getDisplayList() {
- return (DisplayList*) getInt();
- }
-
- inline float getFloat() {
- return mReader.readScalar();
- }
-
- int32_t* getInts(uint32_t& count) {
- count = getInt();
- return (int32_t*) mReader.skip(count * sizeof(int32_t));
- }
-
- uint32_t* getUInts(int8_t& count) {
- count = getInt();
- return (uint32_t*) mReader.skip(count * sizeof(uint32_t));
- }
-
- float* getFloats(int32_t& count) {
- count = getInt();
- return (float*) mReader.skip(count * sizeof(float));
- }
-
- void getText(TextContainer* text) {
- size_t length = text->mByteLength = getInt();
- text->mText = (const char*) mReader.skip(length);
- }
-
Vector<SkBitmap*> mBitmapResources;
Vector<SkBitmap*> mOwnedBitmapResources;
Vector<SkiaColorFilter*> mFilterResources;
@@ -507,7 +395,7 @@
Vector<SkiaShader*> mShaders;
Vector<Layer*> mLayers;
- mutable SkFlattenableReadBuffer mReader;
+ sp<DisplayListData> mDisplayListData;
size_t mSize;
@@ -634,8 +522,8 @@
ANDROID_API void reset();
- const SkWriter32& writeStream() const {
- return mWriter;
+ sp<DisplayListData> getDisplayListData() const {
+ return mDisplayListData;
}
const Vector<SkBitmap*>& getBitmapResources() const {
@@ -683,102 +571,32 @@
}
private:
- void insertRestoreToCount() {
- if (mRestoreSaveCount >= 0) {
- mWriter.writeInt(DisplayList::RestoreToCount);
- addInt(mRestoreSaveCount);
- mRestoreSaveCount = -1;
- }
- }
+ void insertRestoreToCount();
+ void insertTranslate();
- void insertTranlate() {
- if (mHasTranslate) {
- if (mTranslateX != 0.0f || mTranslateY != 0.0f) {
- mWriter.writeInt(DisplayList::Translate);
- addPoint(mTranslateX, mTranslateY);
- mTranslateX = mTranslateY = 0.0f;
- }
- mHasTranslate = false;
- }
- }
-
- inline void addOp(const DisplayList::Op drawOp) {
+ LinearAllocator& alloc() { return mDisplayListData->allocator; }
+ void addStateOp(StateOp* op);
+ bool addDrawOp(DrawOp* op); // returns true if op not rejected
+ void addOpInternal(DisplayListOp* op) {
insertRestoreToCount();
- insertTranlate();
- mWriter.writeInt(drawOp);
- mHasDrawOps = mHasDrawOps || drawOp >= DisplayList::DrawDisplayList;
+ insertTranslate();
+ mDisplayListData->displayListOps.add(op);
}
- uint32_t* addOp(const DisplayList::Op drawOp, const bool reject) {
- insertRestoreToCount();
- insertTranlate();
- mHasDrawOps = mHasDrawOps || drawOp >= DisplayList::DrawDisplayList;
- if (reject) {
- mWriter.writeInt(OP_MAY_BE_SKIPPED_MASK | drawOp);
- mWriter.writeInt(0xdeaddead);
- mBufferSize = mWriter.size();
- return mWriter.peek32(mBufferSize - sizeof(int32_t));
- }
- mWriter.writeInt(drawOp);
- return NULL;
+ template<class T>
+ inline T* refBuffer(const T* srcBuffer, int32_t count) {
+ if (srcBuffer == NULL) return NULL;
+ T* dstBuffer = (T*) mDisplayListData->allocator.alloc(count * sizeof(T));
+ memcpy(dstBuffer, srcBuffer, count * sizeof(T));
+ return dstBuffer;
}
- inline void addSkip(uint32_t* location) {
- if (location) {
- *location = (int32_t) (mWriter.size() - mBufferSize);
- }
+ inline char* refText(const char* text, size_t byteLength) {
+ return (char*) refBuffer<uint8_t>((uint8_t*)text, byteLength);
}
- inline void addInt(int32_t value) {
- mWriter.writeInt(value);
- }
-
- inline void addSize(uint32_t w, uint32_t h) {
- mWriter.writeInt(w);
- mWriter.writeInt(h);
- }
-
- void addInts(const int32_t* values, uint32_t count) {
- mWriter.writeInt(count);
- mWriter.write(values, count * sizeof(int32_t));
- }
-
- void addUInts(const uint32_t* values, int8_t count) {
- mWriter.writeInt(count);
- mWriter.write(values, count * sizeof(uint32_t));
- }
-
- inline void addFloat(float value) {
- mWriter.writeScalar(value);
- }
-
- void addFloats(const float* values, int32_t count) {
- mWriter.writeInt(count);
- mWriter.write(values, count * sizeof(float));
- }
-
- inline void addPoint(float x, float y) {
- mWriter.writeScalar(x);
- mWriter.writeScalar(y);
- }
-
- inline void addBounds(float left, float top, float right, float bottom) {
- mWriter.writeScalar(left);
- mWriter.writeScalar(top);
- mWriter.writeScalar(right);
- mWriter.writeScalar(bottom);
- }
-
- inline void addText(const void* text, size_t byteLength) {
- mWriter.writeInt(byteLength);
- mWriter.writePad(text, byteLength);
- }
-
- inline void addPath(SkPath* path) {
- if (!path) {
- addInt((int) NULL);
- return;
- }
+ inline SkPath* refPath(SkPath* path) {
+ if (!path) return NULL;
SkPath* pathCopy = mPathMap.valueFor(path);
if (pathCopy == NULL || pathCopy->getGenerationID() != path->getGenerationID()) {
@@ -792,13 +610,11 @@
mCaches.resourceCache.incrementRefcount(path);
mSourcePaths.add(path);
}
-
- addInt((int) pathCopy);
+ return pathCopy;
}
- inline SkPaint* addPaint(SkPaint* paint) {
+ inline SkPaint* refPaint(SkPaint* paint) {
if (!paint) {
- addInt((int) NULL);
return paint;
}
@@ -810,14 +626,11 @@
mPaints.add(paintCopy);
}
- addInt((int) paintCopy);
-
return paintCopy;
}
- inline SkRegion* addRegion(SkRegion* region) {
+ inline SkRegion* refRegion(SkRegion* region) {
if (!region) {
- addInt((int) NULL);
return region;
}
@@ -830,53 +643,35 @@
mRegions.add(regionCopy);
}
- addInt((int) regionCopy);
-
return regionCopy;
}
- inline void addDisplayList(DisplayList* displayList) {
- // TODO: To be safe, the display list should be ref-counted in the
- // resources cache, but we rely on the caller (UI toolkit) to
- // do the right thing for now
- addInt((int) displayList);
- }
-
- inline void addMatrix(SkMatrix* matrix) {
+ inline SkMatrix* refMatrix(SkMatrix* matrix) {
// Copying the matrix is cheap and prevents against the user changing the original
// matrix before the operation that uses it
SkMatrix* copy = new SkMatrix(*matrix);
- addInt((int) copy);
mMatrices.add(copy);
+ return copy;
}
- inline void addLayer(Layer* layer) {
- addInt((int) layer);
- mLayers.add(layer);
- mCaches.resourceCache.incrementRefcount(layer);
- }
-
- inline void addBitmap(SkBitmap* bitmap) {
+ inline SkBitmap* refBitmap(SkBitmap* bitmap) {
// Note that this assumes the bitmap is immutable. There are cases this won't handle
// correctly, such as creating the bitmap from scratch, drawing with it, changing its
// contents, and drawing again. The only fix would be to always copy it the first time,
// which doesn't seem worth the extra cycles for this unlikely case.
- addInt((int) bitmap);
mBitmapResources.add(bitmap);
mCaches.resourceCache.incrementRefcount(bitmap);
+ return bitmap;
}
- void addBitmapData(SkBitmap* bitmap) {
- addInt((int) bitmap);
+ inline SkBitmap* refBitmapData(SkBitmap* bitmap) {
mOwnedBitmapResources.add(bitmap);
mCaches.resourceCache.incrementRefcount(bitmap);
+ return bitmap;
}
- inline void addShader(SkiaShader* shader) {
- if (!shader) {
- addInt((int) NULL);
- return;
- }
+ inline SkiaShader* refShader(SkiaShader* shader) {
+ if (!shader) return NULL;
SkiaShader* shaderCopy = mShaderMap.valueFor(shader);
// TODO: We also need to handle generation ID changes in compose shaders
@@ -887,14 +682,13 @@
mShaders.add(shaderCopy);
mCaches.resourceCache.incrementRefcount(shaderCopy);
}
-
- addInt((int) shaderCopy);
+ return shaderCopy;
}
- inline void addColorFilter(SkiaColorFilter* colorFilter) {
- addInt((int) colorFilter);
+ inline SkiaColorFilter* refColorFilter(SkiaColorFilter* colorFilter) {
mFilterResources.add(colorFilter);
mCaches.resourceCache.incrementRefcount(colorFilter);
+ return colorFilter;
}
Vector<SkBitmap*> mBitmapResources;
@@ -919,12 +713,10 @@
Vector<Layer*> mLayers;
- uint32_t mBufferSize;
-
int mRestoreSaveCount;
Caches& mCaches;
- SkWriter32 mWriter;
+ sp<DisplayListData> mDisplayListData;
float mTranslateX;
float mTranslateY;
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index a551b3f..e8a85fd 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -277,6 +277,10 @@
* Texture coordinates of the layer.
*/
Rect texCoords;
+ /**
+ * Clipping rectangle.
+ */
+ Rect clipRect;
/**
* Dirty region indicating what parts of the layer
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 61bedbb..c8a8ed4 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -62,6 +62,7 @@
android::Rect r(dirty.left, dirty.top, dirty.right, dirty.bottom);
mLayer->region.subtractSelf(r);
}
+ mLayer->clipRect.set(dirty);
return OpenGLRenderer::prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom, opaque);
}
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index f55bc9d..7772f3a 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -169,8 +169,8 @@
return prepareDirty(0.0f, 0.0f, mWidth, mHeight, opaque);
}
-status_t OpenGLRenderer::prepareDirty(float left, float top, float right, float bottom,
- bool opaque) {
+status_t OpenGLRenderer::prepareDirty(float left, float top,
+ float right, float bottom, bool opaque) {
mCaches.clearGarbage();
mSnapshot = new Snapshot(mFirstSnapshot,
@@ -207,7 +207,7 @@
if (mCaches.extensions.hasDiscardFramebuffer() &&
left <= 0.0f && top <= 0.0f && right >= mWidth && bottom >= mHeight) {
const GLenum attachments[] = { getTargetFbo() == 0 ? (const GLenum) GL_COLOR_EXT :
- (const GLenum) GL_COLOR_ATTACHMENT0 };
+ (const GLenum) GL_COLOR_ATTACHMENT0, GL_STENCIL_EXT };
glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, attachments);
}
}
@@ -237,12 +237,18 @@
void OpenGLRenderer::startTiling(const sp<Snapshot>& s, bool opaque) {
if (!mSuppressTiling) {
Rect* clip = mTilingSnapshot->clipRect;
- if (s->flags & Snapshot::kFlagIsFboLayer) {
- clip = s->clipRect;
+ if (s->flags & Snapshot::kFlagFboTarget) {
+ clip = &s->layer->clipRect;
}
- mCaches.startTiling(clip->left, s->height - clip->bottom,
- clip->right - clip->left, clip->bottom - clip->top, opaque);
+ startTiling(*clip, s->height, opaque);
+ }
+}
+
+void OpenGLRenderer::startTiling(const Rect& clip, int windowHeight, bool opaque) {
+ if (!mSuppressTiling) {
+ mCaches.startTiling(clip.left, windowHeight - clip.bottom,
+ clip.right - clip.left, clip.bottom - clip.top, opaque);
}
}
@@ -782,18 +788,17 @@
}
bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLuint previousFbo) {
+ layer->clipRect.set(clip);
layer->setFbo(mCaches.fboCache.get());
mSnapshot->region = &mSnapshot->layer->region;
- mSnapshot->flags |= Snapshot::kFlagFboTarget;
-
- mSnapshot->flags |= Snapshot::kFlagIsFboLayer;
+ mSnapshot->flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer |
+ Snapshot::kFlagDirtyOrtho;
mSnapshot->fbo = layer->getFbo();
mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
mSnapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight());
mSnapshot->height = bounds.getHeight();
- mSnapshot->flags |= Snapshot::kFlagDirtyOrtho;
mSnapshot->orthoMatrix.load(mOrthoMatrix);
endTiling();
@@ -811,7 +816,7 @@
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
layer->getTexture(), 0);
- startTiling(mSnapshot);
+ startTiling(mSnapshot, !layer->isBlend());
// Clear the FBO, expand the clear region by 1 to get nice bilinear filtering
mCaches.enableScissor();
@@ -1096,7 +1101,7 @@
rects.push(r.fTop);
rects.push(r.fRight);
rects.push(r.fBottom);
- count++;
+ count += 4;
it.next();
}
@@ -1245,6 +1250,11 @@
void OpenGLRenderer::attachStencilBufferToLayer(Layer* layer) {
// The layer's FBO is already bound when we reach this stage
if (!layer->getStencilRenderBuffer()) {
+ // GL_QCOM_tiled_rendering doesn't like it if a renderbuffer
+ // is attached after we initiated tiling. We must turn it off,
+ // attach the new render buffer then turn tiling back on
+ endTiling();
+
// TODO: See Layer::removeFbo(). The stencil renderbuffer should be cached
GLuint buffer;
glGenRenderbuffers(1, &buffer);
@@ -1254,6 +1264,8 @@
layer->allocateStencilRenderBuffer();
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, buffer);
+
+ startTiling(layer->clipRect, layer->layer.getHeight(), !layer->isBlend());
}
}
@@ -1732,7 +1744,7 @@
void OpenGLRenderer::outputDisplayList(DisplayList* displayList, uint32_t level) {
if (displayList) {
- displayList->output(*this, level);
+ displayList->output(level);
}
}
@@ -2082,7 +2094,6 @@
*/
void OpenGLRenderer::drawConvexPath(const SkPath& path, SkPaint* paint) {
int color = paint->getColor();
- SkPaint::Style style = paint->getStyle();
SkXfermode::Mode mode = getXfermode(paint->getXfermode());
bool isAA = paint->isAntiAlias();
@@ -3193,8 +3204,7 @@
Vertex mesh[count * 6];
Vertex* vertex = mesh;
- for (int i = 0; i < count; i++) {
- int index = i * 4;
+ for (int index = 0; index < count; index += 4) {
float l = rects[index + 0];
float t = rects[index + 1];
float r = rects[index + 2];
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index d4e1eb5..594580e 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -400,12 +400,21 @@
/**
* Tells the GPU what part of the screen is about to be redrawn.
+ * This method will use the clip rect that we started drawing the
+ * frame with.
* This method needs to be invoked every time getTargetFbo() is
* bound again.
*/
void startTiling(const sp<Snapshot>& snapshot, bool opaque = false);
/**
+ * Tells the GPU what part of the screen is about to be redrawn.
+ * This method needs to be invoked every time getTargetFbo() is
+ * bound again.
+ */
+ void startTiling(const Rect& clip, int windowHeight, bool opaque = false);
+
+ /**
* Tells the GPU that we are done drawing the frame or that we
* are switching to another render target.
*/
diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp
index e490151..45c619e 100644
--- a/libs/hwui/Patch.cpp
+++ b/libs/hwui/Patch.cpp
@@ -122,8 +122,6 @@
float rescaleX = 1.0f;
float rescaleY = 1.0f;
- const float meshWidth = right - left;
-
if (xStretchCount > 0) {
uint32_t stretchSize = 0;
for (uint32_t i = 1; i < mXCount; i += 2) {
diff --git a/libs/hwui/PathRenderer.cpp b/libs/hwui/PathRenderer.cpp
index dd13d79..d59e36f 100644
--- a/libs/hwui/PathRenderer.cpp
+++ b/libs/hwui/PathRenderer.cpp
@@ -596,7 +596,6 @@
SkPath::Iter iter(path, forceClose);
SkPoint pts[4];
SkPath::Verb v;
- Vertex* newVertex = 0;
while (SkPath::kDone_Verb != (v = iter.next(pts))) {
switch (v) {
case SkPath::kMove_Verb:
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index 80f39ff..5f4bb5a 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -171,22 +171,19 @@
}
private:
- static inline float min(float a, float b) { return (a < b) ? a : b; }
- static inline float max(float a, float b) { return (a > b) ? a : b; }
-
void intersectWith(Rect& tmp) const {
- tmp.left = max(left, tmp.left);
- tmp.top = max(top, tmp.top);
- tmp.right = min(right, tmp.right);
- tmp.bottom = min(bottom, tmp.bottom);
+ tmp.left = fmaxf(left, tmp.left);
+ tmp.top = fmaxf(top, tmp.top);
+ tmp.right = fminf(right, tmp.right);
+ tmp.bottom = fminf(bottom, tmp.bottom);
}
Rect intersectWith(float l, float t, float r, float b) const {
Rect tmp;
- tmp.left = max(left, l);
- tmp.top = max(top, t);
- tmp.right = min(right, r);
- tmp.bottom = min(bottom, b);
+ tmp.left = fmaxf(left, l);
+ tmp.top = fmaxf(top, t);
+ tmp.right = fminf(right, r);
+ tmp.bottom = fminf(bottom, b);
return tmp;
}
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index c40e26d..48edc73 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -189,7 +189,7 @@
<string name="quick_settings_location_label" msgid="3292451598267467545">"الموقع المستخدم"</string>
<string name="quick_settings_media_device_label" msgid="1302906836372603762">"جهاز الوسائط"</string>
<string name="quick_settings_rssi_label" msgid="7725671335550695589">"RSSI"</string>
- <string name="quick_settings_rssi_emergency_only" msgid="2713774041672886750">"مكالمات الطوارئ فقط"</string>
+ <string name="quick_settings_rssi_emergency_only" msgid="2713774041672886750">"مكالمات طوارئ فقط"</string>
<string name="quick_settings_settings_label" msgid="5326556592578065401">"الإعدادات"</string>
<string name="quick_settings_time_label" msgid="4635969182239736408">"الوقت"</string>
<string name="quick_settings_user_label" msgid="5238995632130897840">"أنا"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 91214d1..7666f7d 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -197,8 +197,8 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Ej ansluten"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Inget nätverk"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi är inaktiverat"</string>
- <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Wi-Fi visas"</string>
- <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Trådlös visning"</string>
+ <string name="quick_settings_wifi_display_label" msgid="6893592964463624333">"Trådlös skärm"</string>
+ <string name="quick_settings_wifi_display_no_connection_label" msgid="2355298740765736918">"Trådlös skärm"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Ljusstyrka"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="status_bar_help_title" msgid="1199237744086469217">"Meddelanden visas här"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 7750e77..f43eabe 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -35,10 +35,10 @@
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Поточні"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Сповіщення"</string>
<string name="battery_low_title" msgid="2783104807551211639">"Підключіть зарядний пристрій"</string>
- <string name="battery_low_subtitle" msgid="1752040062087829196">"Батарея виснажується."</string>
+ <string name="battery_low_subtitle" msgid="1752040062087829196">"Акумулятор розряджається."</string>
<string name="battery_low_percent_format" msgid="1077244949318261761">"Залишилося <xliff:g id="NUMBER">%d%%</xliff:g>"</string>
<string name="invalid_charger" msgid="4549105996740522523">"Заряджання USB не підтримується."\n"Використовуйте лише наданий у комплекті зарядний пристрій."</string>
- <string name="battery_low_why" msgid="7279169609518386372">"Викор. батареї"</string>
+ <string name="battery_low_why" msgid="7279169609518386372">"Використання акумулятора"</string>
<string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Налаштування"</string>
<string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
<string name="status_bar_settings_airplane" msgid="4879879698500955300">"Режим польоту"</string>
@@ -85,11 +85,11 @@
<string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Збільшення екрана."</string>
<string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth під’єднано."</string>
<string name="accessibility_bluetooth_disconnected" msgid="7416648669976870175">"Bluetooth від’єднано."</string>
- <string name="accessibility_no_battery" msgid="358343022352820946">"Немає заряду батареї."</string>
- <string name="accessibility_battery_one_bar" msgid="7774887721891057523">"Одна смужка заряду батареї."</string>
- <string name="accessibility_battery_two_bars" msgid="8500650438735009973">"Дві смужки заряду батареї."</string>
- <string name="accessibility_battery_three_bars" msgid="2302983330865040446">"Три смужки заряду батареї."</string>
- <string name="accessibility_battery_full" msgid="8909122401720158582">"Повний заряд батареї"</string>
+ <string name="accessibility_no_battery" msgid="358343022352820946">"Акумулятор розряджений."</string>
+ <string name="accessibility_battery_one_bar" msgid="7774887721891057523">"Заряд акумулятора: одна смужка."</string>
+ <string name="accessibility_battery_two_bars" msgid="8500650438735009973">"Заряд акумулятора: дві смужки."</string>
+ <string name="accessibility_battery_three_bars" msgid="2302983330865040446">"Заряд акумулятора: три смужки."</string>
+ <string name="accessibility_battery_full" msgid="8909122401720158582">"Акумулятор заряджений."</string>
<string name="accessibility_no_phone" msgid="4894708937052611281">"Немає сигналу телефону."</string>
<string name="accessibility_phone_one_bar" msgid="687699278132664115">"Одна смужка сигналу телефону."</string>
<string name="accessibility_phone_two_bars" msgid="8384905382804815201">"Дві смужки сигналу телефону."</string>
@@ -134,7 +134,7 @@
<string name="accessibility_no_sim" msgid="8274017118472455155">"Немає SIM-карти."</string>
<string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"Прив’язка Bluetooth."</string>
<string name="accessibility_airplane_mode" msgid="834748999790763092">"Режим польоту."</string>
- <string name="accessibility_battery_level" msgid="7451474187113371965">"Відсотків батареї: <xliff:g id="NUMBER">%d</xliff:g>."</string>
+ <string name="accessibility_battery_level" msgid="7451474187113371965">"Заряд акумулятора: <xliff:g id="NUMBER">%d</xliff:g>."</string>
<string name="accessibility_settings_button" msgid="799583911231893380">"Налаштування системи."</string>
<string name="accessibility_notifications_button" msgid="4498000369779421892">"Сповіщення."</string>
<string name="accessibility_remove_notification" msgid="3603099514902182350">"Очистити сповіщення."</string>
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 0d409d5..d1fb2b0 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -4141,6 +4141,11 @@
return rotation == mPortraitRotation || rotation == mUpsideDownRotation;
}
+ public int getUserRotationMode() {
+ return Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.USER_ROTATION, WindowManagerPolicy.USER_ROTATION_FREE,
+ UserHandle.USER_CURRENT);
+ }
// User rotation: to be used when all else fails in assigning an orientation to the device
public void setUserRotationMode(int mode, int rot) {
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 0f14265..1a8641b 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -511,7 +511,8 @@
}
}
- buildInputMethodListLocked(mMethodList, mMethodMap);
+ buildInputMethodListLocked(
+ mMethodList, mMethodMap, false /* resetDefaultEnabledIme */);
boolean changed = false;
@@ -671,9 +672,13 @@
// Just checking if defaultImiId is empty or not
final String defaultImiId = mSettings.getSelectedInputMethod();
+ if (DEBUG) {
+ Slog.d(TAG, "Initial default ime = " + defaultImiId);
+ }
mImeSelectedOnBoot = !TextUtils.isEmpty(defaultImiId);
- buildInputMethodListLocked(mMethodList, mMethodMap);
+ buildInputMethodListLocked(mMethodList, mMethodMap,
+ !mImeSelectedOnBoot /* resetDefaultEnabledIme */);
mSettings.enableAllIMEsIfThereIsNoEnabledIME();
if (!mImeSelectedOnBoot) {
@@ -726,7 +731,7 @@
}
}
- private void resetAllInternalStateLocked(boolean updateOnlyWhenLocaleChanged) {
+ private void resetAllInternalStateLocked(final boolean updateOnlyWhenLocaleChanged) {
if (!mSystemReady) {
// not system ready
return;
@@ -744,7 +749,8 @@
}
// InputMethodAndSubtypeListManager should be reset when the locale is changed.
mImListManager = new InputMethodAndSubtypeListManager(mContext, this);
- buildInputMethodListLocked(mMethodList, mMethodMap);
+ buildInputMethodListLocked(mMethodList, mMethodMap,
+ updateOnlyWhenLocaleChanged /* resetDefaultEnabledIme */);
if (!updateOnlyWhenLocaleChanged) {
final String selectedImiId = mSettings.getSelectedInputMethod();
if (TextUtils.isEmpty(selectedImiId)) {
@@ -814,7 +820,8 @@
mWindowManagerService.setOnHardKeyboardStatusChangeListener(
mHardKeyboardListener);
}
- buildInputMethodListLocked(mMethodList, mMethodMap);
+ buildInputMethodListLocked(mMethodList, mMethodMap,
+ !mImeSelectedOnBoot /* resetDefaultEnabledIme */);
if (!mImeSelectedOnBoot) {
Slog.w(TAG, "Reset the default IME as \"Resource\" is ready here.");
checkCurrentLocaleChangedLocked();
@@ -2147,7 +2154,8 @@
mFileManager.addInputMethodSubtypes(imi, subtypes);
final long ident = Binder.clearCallingIdentity();
try {
- buildInputMethodListLocked(mMethodList, mMethodMap);
+ buildInputMethodListLocked(mMethodList, mMethodMap,
+ false /* resetDefaultEnabledIme */);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2397,9 +2405,10 @@
}
void buildInputMethodListLocked(ArrayList<InputMethodInfo> list,
- HashMap<String, InputMethodInfo> map) {
+ HashMap<String, InputMethodInfo> map, boolean resetDefaultEnabledIme) {
if (DEBUG) {
- Slog.d(TAG, "--- re-buildInputMethodList " + ", \n ------ \n" + getStackTrace());
+ Slog.d(TAG, "--- re-buildInputMethodList reset = " + resetDefaultEnabledIme
+ + " \n ------ \n" + getStackTrace());
}
list.clear();
map.clear();
@@ -2436,14 +2445,8 @@
final String id = p.getId();
map.put(id, p);
- // Valid system default IMEs and IMEs that have English subtypes are enabled
- // by default
- if (InputMethodUtils.isDefaultEnabledIme(mSystemReady, p, mContext)) {
- setInputMethodEnabledLocked(id, true);
- }
-
if (DEBUG) {
- Slog.d(TAG, "Found a third-party input method " + p);
+ Slog.d(TAG, "Found an input method " + p);
}
} catch (XmlPullParserException e) {
@@ -2453,6 +2456,18 @@
}
}
+ if (resetDefaultEnabledIme) {
+ final ArrayList<InputMethodInfo> defaultEnabledIme =
+ InputMethodUtils.getDefaultEnabledImes(mContext, mSystemReady, list);
+ for (int i = 0; i < defaultEnabledIme.size(); ++i) {
+ final InputMethodInfo imi = defaultEnabledIme.get(i);
+ if (DEBUG) {
+ Slog.d(TAG, "--- enable ime = " + imi);
+ }
+ setInputMethodEnabledLocked(imi.getId(), true);
+ }
+ }
+
final String defaultImiId = mSettings.getSelectedInputMethod();
if (!TextUtils.isEmpty(defaultImiId)) {
if (!map.containsKey(defaultImiId)) {
diff --git a/services/java/com/android/server/PreferredComponent.java b/services/java/com/android/server/PreferredComponent.java
index 718b05d..bb22545 100644
--- a/services/java/com/android/server/PreferredComponent.java
+++ b/services/java/com/android/server/PreferredComponent.java
@@ -164,17 +164,19 @@
return mParseError;
}
- public void writeToXml(XmlSerializer serializer) throws IOException {
+ public void writeToXml(XmlSerializer serializer, boolean full) throws IOException {
final int NS = mSetClasses != null ? mSetClasses.length : 0;
serializer.attribute(null, "name", mShortComponent);
- if (mMatch != 0) {
- serializer.attribute(null, "match", Integer.toHexString(mMatch));
- }
- serializer.attribute(null, "set", Integer.toString(NS));
- for (int s=0; s<NS; s++) {
- serializer.startTag(null, "set");
- serializer.attribute(null, "name", mSetComponents[s]);
- serializer.endTag(null, "set");
+ if (full) {
+ if (mMatch != 0) {
+ serializer.attribute(null, "match", Integer.toHexString(mMatch));
+ }
+ serializer.attribute(null, "set", Integer.toString(NS));
+ for (int s=0; s<NS; s++) {
+ serializer.startTag(null, "set");
+ serializer.attribute(null, "name", mSetComponents[s]);
+ serializer.endTag(null, "set");
+ }
}
}
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 0725df0..b7c3450 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -17,7 +17,6 @@
package com.android.server.accessibility;
import static android.accessibilityservice.AccessibilityServiceInfo.DEFAULT;
-import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
import android.Manifest;
import android.accessibilityservice.AccessibilityService;
@@ -518,6 +517,10 @@
ComponentName componentName = new ComponentName("foo.bar",
"AutomationAccessibilityService");
synchronized (mLock) {
+ if (mUiAutomationService != null) {
+ throw new IllegalStateException("UiAutomationService " + serviceClient
+ + "already registered!");
+ }
// If an automation services is connected to the system all services are stopped
// so the automation one is the only one running. Settings are not changed so when
// the automation service goes away the state is restored from the settings.
@@ -556,7 +559,8 @@
synchronized (mLock) {
UserState userState = getCurrentUserStateLocked();
// Stash the old state so we can restore it when the keyguard is gone.
- mTempStateChangeForCurrentUserMemento.initialize(mCurrentUserId, getCurrentUserStateLocked());
+ mTempStateChangeForCurrentUserMemento.initialize(mCurrentUserId,
+ getCurrentUserStateLocked());
// Set the temporary state.
userState.mIsAccessibilityEnabled = true;
userState.mIsTouchExplorationEnabled= touchExplorationEnabled;
@@ -579,6 +583,9 @@
&& serviceClient != null && mUiAutomationService.mServiceInterface
.asBinder() == serviceClient.asBinder()) {
mUiAutomationService.binderDied();
+ } else {
+ throw new IllegalStateException("UiAutomationService " + serviceClient
+ + " not registered!");
}
}
}
@@ -935,7 +942,8 @@
}
if (!event.isImportantForAccessibility()
- && !service.mIncludeNotImportantViews) {
+ && (service.mFetchFlags
+ & AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) == 0) {
return false;
}
@@ -1486,7 +1494,7 @@
boolean mRequestTouchExplorationMode;
- boolean mIncludeNotImportantViews;
+ int mFetchFlags;
long mNotificationTimeout;
@@ -1565,10 +1573,15 @@
if (mIsAutomation || info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion
>= Build.VERSION_CODES.JELLY_BEAN) {
- mIncludeNotImportantViews =
- (info.flags & FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0;
+ mFetchFlags |= (info.flags
+ & AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0 ?
+ AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS : 0;
}
+ mFetchFlags |= (info.flags
+ & AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS) != 0 ?
+ AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS : 0;
+
mRequestTouchExplorationMode = (info.flags
& AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0;
@@ -1664,8 +1677,8 @@
}
@Override
- public boolean findAccessibilityNodeInfoByViewId(int accessibilityWindowId,
- long accessibilityNodeId, int viewId, int interactionId,
+ public boolean findAccessibilityNodeInfosByViewId(int accessibilityWindowId,
+ long accessibilityNodeId, String viewIdResName, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
final int resolvedWindowId;
@@ -1689,14 +1702,13 @@
}
}
}
- final int flags = (mIncludeNotImportantViews) ?
- AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0;
final int interrogatingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity();
MagnificationSpec spec = getCompatibleMagnificationSpec(resolvedWindowId);
try {
- connection.findAccessibilityNodeInfoByViewId(accessibilityNodeId, viewId,
- interactionId, callback, flags, interrogatingPid, interrogatingTid, spec);
+ connection.findAccessibilityNodeInfosByViewId(accessibilityNodeId,
+ viewIdResName, interactionId, callback, mFetchFlags, interrogatingPid,
+ interrogatingTid, spec);
return true;
} catch (RemoteException re) {
if (DEBUG) {
@@ -1735,14 +1747,13 @@
}
}
}
- final int flags = (mIncludeNotImportantViews) ?
- AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0;
final int interrogatingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity();
MagnificationSpec spec = getCompatibleMagnificationSpec(resolvedWindowId);
try {
connection.findAccessibilityNodeInfosByText(accessibilityNodeId, text,
- interactionId, callback, flags, interrogatingPid, interrogatingTid, spec);
+ interactionId, callback, mFetchFlags, interrogatingPid, interrogatingTid,
+ spec);
return true;
} catch (RemoteException re) {
if (DEBUG) {
@@ -1781,15 +1792,13 @@
}
}
}
- final int allFlags = flags | ((mIncludeNotImportantViews) ?
- AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0);
final int interrogatingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity();
MagnificationSpec spec = getCompatibleMagnificationSpec(resolvedWindowId);
try {
connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId,
- interactionId, callback, allFlags, interrogatingPid, interrogatingTid,
- spec);
+ interactionId, callback, mFetchFlags | flags, interrogatingPid,
+ interrogatingTid, spec);
return true;
} catch (RemoteException re) {
if (DEBUG) {
@@ -1828,14 +1837,12 @@
}
}
}
- final int flags = (mIncludeNotImportantViews) ?
- AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0;
final int interrogatingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity();
MagnificationSpec spec = getCompatibleMagnificationSpec(resolvedWindowId);
try {
connection.findFocus(accessibilityNodeId, focusType, interactionId, callback,
- flags, interrogatingPid, interrogatingTid, spec);
+ mFetchFlags, interrogatingPid, interrogatingTid, spec);
return true;
} catch (RemoteException re) {
if (DEBUG) {
@@ -1874,14 +1881,12 @@
}
}
}
- final int flags = (mIncludeNotImportantViews) ?
- AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0;
final int interrogatingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity();
MagnificationSpec spec = getCompatibleMagnificationSpec(resolvedWindowId);
try {
connection.focusSearch(accessibilityNodeId, direction, interactionId, callback,
- flags, interrogatingPid, interrogatingTid, spec);
+ mFetchFlags, interrogatingPid, interrogatingTid, spec);
return true;
} catch (RemoteException re) {
if (DEBUG) {
@@ -1920,13 +1925,11 @@
}
}
}
- final int flags = (mIncludeNotImportantViews) ?
- AccessibilityNodeInfo.INCLUDE_NOT_IMPORTANT_VIEWS : 0;
final int interrogatingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity();
try {
connection.performAccessibilityAction(accessibilityNodeId, action, arguments,
- interactionId, callback, flags, interrogatingPid, interrogatingTid);
+ interactionId, callback, mFetchFlags, interrogatingPid, interrogatingTid);
} catch (RemoteException re) {
if (DEBUG) {
Slog.e(LOG_TAG, "Error calling performAccessibilityAction()");
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index e0046ad..ca60a93 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -51,6 +51,7 @@
import android.app.IServiceConnection;
import android.app.IStopUserCallback;
import android.app.IThumbnailReceiver;
+import android.app.IUiAutomationConnection;
import android.app.IUserSwitchObserver;
import android.app.Instrumentation;
import android.app.Notification;
@@ -162,7 +163,7 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
-public final class ActivityManagerService extends ActivityManagerNative
+public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
private static final String USER_DATA_DIR = "/data/user/";
static final String TAG = "ActivityManager";
@@ -4283,8 +4284,9 @@
}
thread.bindApplication(processName, appInfo, providers,
app.instrumentationClass, profileFile, profileFd, profileAutoStop,
- app.instrumentationArguments, app.instrumentationWatcher, testMode,
- enableOpenGlTrace, isRestrictedBackupMode || !normalMode, app.persistent,
+ app.instrumentationArguments, app.instrumentationWatcher,
+ app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
+ isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(mConfiguration), app.compat, getCommonServicesLocked(),
mCoreSettingsObserver.getCoreSettingsLocked());
updateLruProcessLocked(app, false);
@@ -12228,7 +12230,8 @@
public boolean startInstrumentation(ComponentName className,
String profileFile, int flags, Bundle arguments,
- IInstrumentationWatcher watcher, int userId) {
+ IInstrumentationWatcher watcher, IUiAutomationConnection uiAutomationConnection,
+ int userId) {
enforceNotIsolatedCaller("startInstrumentation");
userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, false, true, "startInstrumentation", null);
@@ -12282,6 +12285,7 @@
app.instrumentationProfileFile = profileFile;
app.instrumentationArguments = arguments;
app.instrumentationWatcher = watcher;
+ app.instrumentationUiAutomationConnection = uiAutomationConnection;
app.instrumentationResultClass = className;
Binder.restoreCallingIdentity(origId);
}
@@ -12324,7 +12328,15 @@
} catch (RemoteException e) {
}
}
+ if (app.instrumentationUiAutomationConnection != null) {
+ try {
+ app.instrumentationUiAutomationConnection.shutdown();
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
app.instrumentationWatcher = null;
+ app.instrumentationUiAutomationConnection = null;
app.instrumentationClass = null;
app.instrumentationInfo = null;
app.instrumentationProfileFile = null;
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 7fbab04..a32af2f 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -22,6 +22,7 @@
import android.app.Dialog;
import android.app.IApplicationThread;
import android.app.IInstrumentationWatcher;
+import android.app.IUiAutomationConnection;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -95,6 +96,7 @@
ApplicationInfo instrumentationInfo; // the application being instrumented
String instrumentationProfileFile; // where to save profiling
IInstrumentationWatcher instrumentationWatcher; // who is waiting
+ IUiAutomationConnection instrumentationUiAutomationConnection; // Connection to use the UI introspection APIs.
Bundle instrumentationArguments;// as given to us
ComponentName instrumentationResultClass;// copy of instrumentationClass
boolean usingWrapper; // Set to true when process was launched with a wrapper attached
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 626002d..829e67a 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -173,7 +173,7 @@
public class PackageManagerService extends IPackageManager.Stub {
static final String TAG = "PackageManager";
static final boolean DEBUG_SETTINGS = false;
- private static final boolean DEBUG_PREFERRED = false;
+ static final boolean DEBUG_PREFERRED = true;
static final boolean DEBUG_UPGRADE = false;
private static final boolean DEBUG_INSTALL = false;
private static final boolean DEBUG_REMOVE = false;
@@ -1021,7 +1021,7 @@
readPermissions();
- mRestoredSettings = mSettings.readLPw(sUserManager.getUsers(false),
+ mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),
mSdkVersion, mOnlyCore);
long startTime = SystemClock.uptimeMillis();
@@ -4967,7 +4967,7 @@
ps.haveGids = true;
}
- private final class ActivityIntentResolver
+ final class ActivityIntentResolver
extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> {
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
boolean defaultOnly, int userId) {
@@ -8830,8 +8830,10 @@
}
}
- if (clearPackagePreferredActivitiesLPw(packageName, UserHandle.getCallingUserId())) {
- scheduleWriteSettingsLocked();
+ int user = UserHandle.getCallingUserId();
+ if (clearPackagePreferredActivitiesLPw(packageName, user)) {
+ mSettings.writePackageRestrictionsLPr(user);
+ scheduleWriteSettingsLocked();
}
}
}
@@ -8849,7 +8851,8 @@
Iterator<PreferredActivity> it = pir.filterIterator();
while (it.hasNext()) {
PreferredActivity pa = it.next();
- if (pa.mPref.mComponent.getPackageName().equals(packageName)) {
+ if (packageName == null ||
+ pa.mPref.mComponent.getPackageName().equals(packageName)) {
if (removed == null) {
removed = new ArrayList<PreferredActivity>();
}
@@ -8862,12 +8865,24 @@
pir.removeFilter(pa);
}
changed = true;
- mSettings.writePackageRestrictionsLPr(thisUserId);
}
}
return changed;
}
+ public void resetPreferredActivities(int userId) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
+ // writer
+ synchronized (mPackages) {
+ int user = UserHandle.getCallingUserId();
+ clearPackagePreferredActivitiesLPw(null, user);
+ mSettings.readDefaultPreferredAppsLPw(this, user);
+ mSettings.writePackageRestrictionsLPr(user);
+ scheduleWriteSettingsLocked();
+ }
+ }
+
public int getPreferredActivities(List<IntentFilter> outFilters,
List<ComponentName> outActivities, String packageName) {
@@ -9254,6 +9269,7 @@
}
DumpState dumpState = new DumpState();
+ boolean fullPreferred = false;
String packageName = null;
@@ -9277,7 +9293,7 @@
pw.println(" r[esolvers]: dump intent resolvers");
pw.println(" perm[issions]: dump permissions");
pw.println(" pref[erred]: print preferred package settings");
- pw.println(" preferred-xml: print preferred package settings as xml");
+ pw.println(" preferred-xml [--full]: print preferred package settings as xml");
pw.println(" prov[iders]: dump content providers");
pw.println(" p[ackages]: dump installed packages");
pw.println(" s[hared-users]: dump shared user IDs");
@@ -9311,6 +9327,10 @@
dumpState.setDump(DumpState.DUMP_PREFERRED);
} else if ("preferred-xml".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_PREFERRED_XML);
+ if (opti < args.length && "--full".equals(args[opti])) {
+ fullPreferred = true;
+ opti++;
+ }
} else if ("p".equals(cmd) || "packages".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_PACKAGES);
} else if ("s".equals(cmd) || "shared-users".equals(cmd)) {
@@ -9405,7 +9425,7 @@
serializer.startDocument(null, true);
serializer.setFeature(
"http://xmlpull.org/v1/doc/features.html#indent-output", true);
- mSettings.writePreferredActivitiesLPr(serializer, 0);
+ mSettings.writePreferredActivitiesLPr(serializer, 0, fullPreferred);
serializer.endDocument();
serializer.flush();
} catch (IllegalArgumentException e) {
@@ -10158,7 +10178,7 @@
/** Called by UserManagerService */
void createNewUserLILPw(int userHandle, File path) {
if (mInstaller != null) {
- mSettings.createNewUserLILPw(mInstaller, userHandle, path);
+ mSettings.createNewUserLILPw(this, mInstaller, userHandle, path);
}
}
diff --git a/services/java/com/android/server/pm/PreferredActivity.java b/services/java/com/android/server/pm/PreferredActivity.java
index dbf56ef..c655bb1 100644
--- a/services/java/com/android/server/pm/PreferredActivity.java
+++ b/services/java/com/android/server/pm/PreferredActivity.java
@@ -46,8 +46,8 @@
mPref = new PreferredComponent(this, parser);
}
- public void writeToXml(XmlSerializer serializer) throws IOException {
- mPref.writeToXml(serializer);
+ public void writeToXml(XmlSerializer serializer, boolean full) throws IOException {
+ mPref.writeToXml(serializer, full);
serializer.startTag(null, "filter");
super.writeToXml(serializer);
serializer.endTag(null, "filter");
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index e336524..13f514b 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -23,6 +23,12 @@
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.os.PatternMatcher;
+import android.util.LogPrinter;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.JournaledFile;
import com.android.internal.util.XmlUtils;
@@ -972,14 +978,14 @@
return components;
}
- void writePreferredActivitiesLPr(XmlSerializer serializer, int userId)
+ void writePreferredActivitiesLPr(XmlSerializer serializer, int userId, boolean full)
throws IllegalArgumentException, IllegalStateException, IOException {
serializer.startTag(null, "preferred-activities");
PreferredIntentResolver pir = mPreferredActivities.get(userId);
if (pir != null) {
for (final PreferredActivity pa : pir.filterSet()) {
serializer.startTag(null, TAG_ITEM);
- pa.writeToXml(serializer);
+ pa.writeToXml(serializer, full);
serializer.endTag(null, TAG_ITEM);
}
}
@@ -1072,7 +1078,7 @@
}
}
- writePreferredActivitiesLPr(serializer, userId);
+ writePreferredActivitiesLPr(serializer, userId, true);
serializer.endTag(null, TAG_PACKAGE_RESTRICTIONS);
@@ -1557,7 +1563,8 @@
}
}
- boolean readLPw(List<UserInfo> users, int sdkVersion, boolean onlyCore) {
+ boolean readLPw(PackageManagerService service, List<UserInfo> users, int sdkVersion,
+ boolean onlyCore) {
FileInputStream str = null;
if (mBackupSettingsFilename.exists()) {
try {
@@ -1588,7 +1595,7 @@
PackageManagerService.reportSettingsProblem(Log.INFO,
"No settings file; creating initial state");
if (!onlyCore) {
- readDefaultPreferredAppsLPw(0);
+ readDefaultPreferredAppsLPw(service, 0);
}
mInternalSdkPlatform = mExternalSdkPlatform = sdkVersion;
return false;
@@ -1771,7 +1778,7 @@
return true;
}
- private void readDefaultPreferredAppsLPw(int userId) {
+ void readDefaultPreferredAppsLPw(PackageManagerService service, int userId) {
// Read preferred apps from .../etc/preferred-apps directory.
File preferredDir = new File(Environment.getRootDirectory(), "etc/preferred-apps");
if (!preferredDir.exists() || !preferredDir.isDirectory()) {
@@ -1793,6 +1800,7 @@
continue;
}
+ if (PackageManagerService.DEBUG_PREFERRED) Log.d(TAG, "Reading default preferred " + f);
FileInputStream str = null;
try {
str = new FileInputStream(f);
@@ -1814,7 +1822,7 @@
+ " does not start with 'preferred-activities'");
continue;
}
- readPreferredActivitiesLPw(parser, userId);
+ readDefaultPreferredActivitiesLPw(service, parser, userId);
} catch (XmlPullParserException e) {
Slog.w(TAG, "Error reading apps file " + f, e);
} catch (IOException e) {
@@ -1830,6 +1838,112 @@
}
}
+ private void readDefaultPreferredActivitiesLPw(PackageManagerService service,
+ XmlPullParser parser, int userId)
+ throws XmlPullParserException, IOException {
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals(TAG_ITEM)) {
+ PreferredActivity tmpPa = new PreferredActivity(parser);
+ if (tmpPa.mPref.getParseError() == null) {
+ // The initial preferences only specify the target activity
+ // component and intent-filter, not the set of matches. So we
+ // now need to query for the matches to build the correct
+ // preferred activity entry.
+ if (PackageManagerService.DEBUG_PREFERRED) {
+ Log.d(TAG, "Processing preferred:");
+ tmpPa.dump(new LogPrinter(Log.DEBUG, TAG), " ");
+ }
+ final ComponentName cn = tmpPa.mPref.mComponent;
+ Intent intent = new Intent();
+ int flags = 0;
+ intent.setAction(tmpPa.getAction(0));
+ for (int i=0; i<tmpPa.countCategories(); i++) {
+ String cat = tmpPa.getCategory(i);
+ if (cat.equals(Intent.CATEGORY_DEFAULT)) {
+ flags |= PackageManager.MATCH_DEFAULT_ONLY;
+ } else {
+ intent.addCategory(cat);
+ }
+ }
+ if (tmpPa.countDataSchemes() > 0) {
+ Uri.Builder builder = new Uri.Builder();
+ builder.scheme(tmpPa.getDataScheme(0));
+ if (tmpPa.countDataAuthorities() > 0) {
+ IntentFilter.AuthorityEntry auth = tmpPa.getDataAuthority(0);
+ if (auth.getHost() != null) {
+ builder.authority(auth.getHost());
+ }
+ }
+ if (tmpPa.countDataPaths() > 0) {
+ PatternMatcher path = tmpPa.getDataPath(0);
+ builder.path(path.getPath());
+ }
+ intent.setData(builder.build());
+ } else if (tmpPa.countDataTypes() > 0) {
+ intent.setType(tmpPa.getDataType(0));
+ }
+ List<ResolveInfo> ri = service.mActivities.queryIntent(intent,
+ intent.getType(), flags, 0);
+ if (PackageManagerService.DEBUG_PREFERRED) Log.d(TAG, "Queried " + intent
+ + " results: " + ri);
+ int match = 0;
+ if (ri != null && ri.size() > 1) {
+ boolean haveAct = false;
+ boolean haveNonSys = false;
+ ComponentName[] set = new ComponentName[ri.size()];
+ for (int i=0; i<ri.size(); i++) {
+ ActivityInfo ai = ri.get(i).activityInfo;
+ set[i] = new ComponentName(ai.packageName, ai.name);
+ if ((ai.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) {
+ // If any of the matches are not system apps, then
+ // there is a third party app that is now an option...
+ // so don't set a default since we don't want to hide it.
+ if (PackageManagerService.DEBUG_PREFERRED) Log.d(TAG, "Result "
+ + ai.packageName + "/" + ai.name + ": non-system!");
+ haveNonSys = true;
+ break;
+ } else if (cn.getPackageName().equals(ai.packageName)
+ && cn.getClassName().equals(ai.name)) {
+ if (PackageManagerService.DEBUG_PREFERRED) Log.d(TAG, "Result "
+ + ai.packageName + "/" + ai.name + ": default!");
+ haveAct = true;
+ match = ri.get(i).match;
+ } else {
+ if (PackageManagerService.DEBUG_PREFERRED) Log.d(TAG, "Result "
+ + ai.packageName + "/" + ai.name + ": skipped");
+ }
+ }
+ if (haveAct && !haveNonSys) {
+ PreferredActivity pa = new PreferredActivity(tmpPa, match, set,
+ tmpPa.mPref.mComponent);
+ editPreferredActivitiesLPw(userId).addFilter(pa);
+ } else if (!haveNonSys) {
+ Slog.w(TAG, "No component found for default preferred activity "
+ + tmpPa.mPref.mComponent);
+ }
+ }
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Error in package manager settings: <preferred-activity> "
+ + tmpPa.mPref.getParseError() + " at "
+ + parser.getPositionDescription());
+ }
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Unknown element under <preferred-activities>: " + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ }
+
private int readInt(XmlPullParser parser, String ns, String name, int defValue) {
String v = parser.getAttributeValue(ns, name);
try {
@@ -2329,7 +2443,8 @@
}
}
- void createNewUserLILPw(Installer installer, int userHandle, File path) {
+ void createNewUserLILPw(PackageManagerService service, Installer installer,
+ int userHandle, File path) {
path.mkdir();
FileUtils.setPermissions(path.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
| FileUtils.S_IXOTH, -1, -1);
@@ -2340,7 +2455,7 @@
installer.createUserData(ps.name,
UserHandle.getUid(userHandle, ps.appId), userHandle);
}
- readDefaultPreferredAppsLPw(userHandle);
+ readDefaultPreferredAppsLPw(service, userHandle);
writePackageRestrictionsLPr(userHandle);
}
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
index c7c2c62..8fa6de5 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -169,7 +169,9 @@
startAccessoryMode();
}
- if ("1".equals(SystemProperties.get("ro.adb.secure"))) {
+ boolean secureAdbEnabled = SystemProperties.getBoolean("ro.adb.secure", false);
+ boolean dataEncrypted = "1".equals(SystemProperties.get("vold.decrypt"));
+ if (secureAdbEnabled && !dataEncrypted) {
mDebuggingManager = new UsbDebuggingManager(context);
}
}
diff --git a/services/java/com/android/server/wm/DisplayMagnifier.java b/services/java/com/android/server/wm/DisplayMagnifier.java
index d3c01f0..6e876f6 100644
--- a/services/java/com/android/server/wm/DisplayMagnifier.java
+++ b/services/java/com/android/server/wm/DisplayMagnifier.java
@@ -228,6 +228,10 @@
return spec;
}
+ public void destroyLocked() {
+ mMagnifedViewport.destroyWindow();
+ }
+
/** NOTE: This has to be called within a surface transaction. */
public void drawMagnifiedRegionBorderIfNeededLocked() {
mMagnifedViewport.drawWindowIfNeededLocked();
@@ -258,7 +262,7 @@
private final int mBorderWidth;
private final int mHalfBorderWidth;
- private ViewportWindow mWindow;
+ private final ViewportWindow mWindow;
private boolean mFullRedrawNeeded;
@@ -459,6 +463,10 @@
mWindow.drawIfNeeded();
}
+ public void destroyWindow() {
+ mWindow.releaseSurface();
+ }
+
private final class ViewportWindow {
private static final String SURFACE_TITLE = "Magnification Overlay";
@@ -640,6 +648,10 @@
}
}
}
+
+ public void releaseSurface() {
+ mSurface.release();
+ }
}
}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index ff2dc0f..1e5cd54 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -3039,7 +3039,10 @@
mDisplayMagnifier = new DisplayMagnifier(this, callbacks);
} else {
if (callbacks == null) {
- mDisplayMagnifier = null;
+ if (mDisplayMagnifier != null) {
+ mDisplayMagnifier.destroyLocked();
+ mDisplayMagnifier = null;
+ }
} else {
throw new IllegalStateException("Magnification callbacks already set!");
}
@@ -5598,6 +5601,11 @@
}
@Override
+ public boolean isRotationFrozen() {
+ return mPolicy.getUserRotationMode() == WindowManagerPolicy.USER_ROTATION_LOCKED;
+ }
+
+ @Override
public int watchRotation(IRotationWatcher watcher) {
final IBinder watcherBinder = watcher.asBinder();
IBinder.DeathRecipient dr = new IBinder.DeathRecipient() {
diff --git a/services/jni/com_android_server_input_InputManagerService.cpp b/services/jni/com_android_server_input_InputManagerService.cpp
index a97becf..57803e3 100644
--- a/services/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/jni/com_android_server_input_InputManagerService.cpp
@@ -975,6 +975,11 @@
static jint nativeInit(JNIEnv* env, jclass clazz,
jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
+ if (messageQueue == NULL) {
+ jniThrowRuntimeException(env, "MessageQueue is not initialized.");
+ return 0;
+ }
+
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
messageQueue->getLooper());
im->incStrong(serviceObj);
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 0f531b7..1ab5f45 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -139,7 +139,7 @@
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
Settings settings = new Settings(getContext(), getContext().getFilesDir());
- assertEquals(true, settings.readLPw(null, 0, false));
+ assertEquals(true, settings.readLPw(null, null, 0, false));
assertNotNull(settings.peekPackageLPr(PACKAGE_NAME_3));
assertNotNull(settings.peekPackageLPr(PACKAGE_NAME_1));
@@ -157,11 +157,11 @@
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
Settings settings = new Settings(getContext(), getContext().getFilesDir());
- assertEquals(true, settings.readLPw(null, 0, false));
+ assertEquals(true, settings.readLPw(null, null, 0, false));
// Create Settings again to make it read from the new files
settings = new Settings(getContext(), getContext().getFilesDir());
- assertEquals(true, settings.readLPw(null, 0, false));
+ assertEquals(true, settings.readLPw(null, null, 0, false));
PackageSetting ps = settings.peekPackageLPr(PACKAGE_NAME_2);
assertEquals(COMPONENT_ENABLED_STATE_DISABLED_USER, ps.getEnabled(0));
@@ -172,7 +172,7 @@
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
Settings settings = new Settings(getContext(), getContext().getFilesDir());
- assertEquals(true, settings.readLPw(null, 0, false));
+ assertEquals(true, settings.readLPw(null, null, 0, false));
// Enable/Disable a package
PackageSetting ps = settings.peekPackageLPr(PACKAGE_NAME_1);
diff --git a/test-runner/src/android/test/AndroidTestRunner.java b/test-runner/src/android/test/AndroidTestRunner.java
index 30876d0..aa7c677 100644
--- a/test-runner/src/android/test/AndroidTestRunner.java
+++ b/test-runner/src/android/test/AndroidTestRunner.java
@@ -21,6 +21,7 @@
import android.os.PerformanceCollector.PerformanceResultsWriter;
import com.google.android.collect.Lists;
+
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestListener;
diff --git a/test-runner/src/android/test/InstrumentationTestRunner.java b/test-runner/src/android/test/InstrumentationTestRunner.java
index 8e30875..91d04da 100644
--- a/test-runner/src/android/test/InstrumentationTestRunner.java
+++ b/test-runner/src/android/test/InstrumentationTestRunner.java
@@ -390,12 +390,11 @@
}
/**
- * Get the Bundle object that contains the arguments
+ * Get the arguments passed to this instrumentation.
*
* @return the Bundle object
- * @hide
*/
- public Bundle getBundle(){
+ public Bundle getArguments() {
return mArguments;
}
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index e2cb65d..6b9f4c3 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -60,7 +60,7 @@
public void testMeasureStartUpTime() throws RemoteException {
InstrumentationTestRunner instrumentation =
(InstrumentationTestRunner)getInstrumentation();
- Bundle args = instrumentation.getBundle();
+ Bundle args = instrumentation.getArguments();
mAm = ActivityManagerNative.getDefault();
createMappings();
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java
index 3d3d709..774811e 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java
@@ -61,6 +61,7 @@
public RegionView(Context c) {
super(c);
+ setAlpha(0.5f);
}
public float getClipPosition() {
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index f012bcb..9f116fc 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -487,4 +487,10 @@
// TODO Auto-generated method stub
return null;
}
+
+ @Override
+ public boolean isRotationFrozen() throws RemoteException {
+ // TODO Auto-generated method stub
+ return false;
+ }
}
diff --git a/voip/java/android/net/rtp/AudioCodec.java b/voip/java/android/net/rtp/AudioCodec.java
deleted file mode 100644
index 85255c8..0000000
--- a/voip/java/android/net/rtp/AudioCodec.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.rtp;
-
-import java.util.Arrays;
-
-/**
- * This class defines a collection of audio codecs to be used with
- * {@link AudioStream}s. Their parameters are designed to be exchanged using
- * Session Description Protocol (SDP). Most of the values listed here can be
- * found in RFC 3551, while others are described in separated standards.
- *
- * <p>Few simple configurations are defined as public static instances for the
- * convenience of direct uses. More complicated ones could be obtained using
- * {@link #getCodec(int, String, String)}. For example, one can use the
- * following snippet to create a mode-1-only AMR codec.</p>
- * <pre>
- * AudioCodec codec = AudioCodec.getCodec(100, "AMR/8000", "mode-set=1");
- * </pre>
- *
- * @see AudioStream
- */
-public class AudioCodec {
- /**
- * The RTP payload type of the encoding.
- */
- public final int type;
-
- /**
- * The encoding parameters to be used in the corresponding SDP attribute.
- */
- public final String rtpmap;
-
- /**
- * The format parameters to be used in the corresponding SDP attribute.
- */
- public final String fmtp;
-
- /**
- * G.711 u-law audio codec.
- */
- public static final AudioCodec PCMU = new AudioCodec(0, "PCMU/8000", null);
-
- /**
- * G.711 a-law audio codec.
- */
- public static final AudioCodec PCMA = new AudioCodec(8, "PCMA/8000", null);
-
- /**
- * GSM Full-Rate audio codec, also known as GSM-FR, GSM 06.10, GSM, or
- * simply FR.
- */
- public static final AudioCodec GSM = new AudioCodec(3, "GSM/8000", null);
-
- /**
- * GSM Enhanced Full-Rate audio codec, also known as GSM-EFR, GSM 06.60, or
- * simply EFR.
- */
- public static final AudioCodec GSM_EFR = new AudioCodec(96, "GSM-EFR/8000", null);
-
- /**
- * Adaptive Multi-Rate narrowband audio codec, also known as AMR or AMR-NB.
- * Currently CRC, robust sorting, and interleaving are not supported. See
- * more details about these features in RFC 4867.
- */
- public static final AudioCodec AMR = new AudioCodec(97, "AMR/8000", null);
-
- private static final AudioCodec[] sCodecs = {GSM_EFR, AMR, GSM, PCMU, PCMA};
-
- private AudioCodec(int type, String rtpmap, String fmtp) {
- this.type = type;
- this.rtpmap = rtpmap;
- this.fmtp = fmtp;
- }
-
- /**
- * Returns system supported audio codecs.
- */
- public static AudioCodec[] getCodecs() {
- return Arrays.copyOf(sCodecs, sCodecs.length);
- }
-
- /**
- * Creates an AudioCodec according to the given configuration.
- *
- * @param type The payload type of the encoding defined in RTP/AVP.
- * @param rtpmap The encoding parameters specified in the corresponding SDP
- * attribute, or null if it is not available.
- * @param fmtp The format parameters specified in the corresponding SDP
- * attribute, or null if it is not available.
- * @return The configured AudioCodec or {@code null} if it is not supported.
- */
- public static AudioCodec getCodec(int type, String rtpmap, String fmtp) {
- if (type < 0 || type > 127) {
- return null;
- }
-
- AudioCodec hint = null;
- if (rtpmap != null) {
- String clue = rtpmap.trim().toUpperCase();
- for (AudioCodec codec : sCodecs) {
- if (clue.startsWith(codec.rtpmap)) {
- String channels = clue.substring(codec.rtpmap.length());
- if (channels.length() == 0 || channels.equals("/1")) {
- hint = codec;
- }
- break;
- }
- }
- } else if (type < 96) {
- for (AudioCodec codec : sCodecs) {
- if (type == codec.type) {
- hint = codec;
- rtpmap = codec.rtpmap;
- break;
- }
- }
- }
-
- if (hint == null) {
- return null;
- }
- if (hint == AMR && fmtp != null) {
- String clue = fmtp.toLowerCase();
- if (clue.contains("crc=1") || clue.contains("robust-sorting=1") ||
- clue.contains("interleaving=")) {
- return null;
- }
- }
- return new AudioCodec(type, rtpmap, fmtp);
- }
-}
diff --git a/voip/java/android/net/rtp/AudioGroup.java b/voip/java/android/net/rtp/AudioGroup.java
deleted file mode 100644
index 8faeb88..0000000
--- a/voip/java/android/net/rtp/AudioGroup.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.rtp;
-
-import android.media.AudioManager;
-
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-
-/**
- * An AudioGroup is an audio hub for the speaker, the microphone, and
- * {@link AudioStream}s. Each of these components can be logically turned on
- * or off by calling {@link #setMode(int)} or {@link RtpStream#setMode(int)}.
- * The AudioGroup will go through these components and process them one by one
- * within its execution loop. The loop consists of four steps. First, for each
- * AudioStream not in {@link RtpStream#MODE_SEND_ONLY}, decodes its incoming
- * packets and stores in its buffer. Then, if the microphone is enabled,
- * processes the recorded audio and stores in its buffer. Third, if the speaker
- * is enabled, mixes all AudioStream buffers and plays back. Finally, for each
- * AudioStream not in {@link RtpStream#MODE_RECEIVE_ONLY}, mixes all other
- * buffers and sends back the encoded packets. An AudioGroup does nothing if
- * there is no AudioStream in it.
- *
- * <p>Few things must be noticed before using these classes. The performance is
- * highly related to the system load and the network bandwidth. Usually a
- * simpler {@link AudioCodec} costs fewer CPU cycles but requires more network
- * bandwidth, and vise versa. Using two AudioStreams at the same time doubles
- * not only the load but also the bandwidth. The condition varies from one
- * device to another, and developers should choose the right combination in
- * order to get the best result.</p>
- *
- * <p>It is sometimes useful to keep multiple AudioGroups at the same time. For
- * example, a Voice over IP (VoIP) application might want to put a conference
- * call on hold in order to make a new call but still allow people in the
- * conference call talking to each other. This can be done easily using two
- * AudioGroups, but there are some limitations. Since the speaker and the
- * microphone are globally shared resources, only one AudioGroup at a time is
- * allowed to run in a mode other than {@link #MODE_ON_HOLD}. The others will
- * be unable to acquire these resources and fail silently.</p>
- *
- * <p class="note">Using this class requires
- * {@link android.Manifest.permission#RECORD_AUDIO} permission. Developers
- * should set the audio mode to {@link AudioManager#MODE_IN_COMMUNICATION}
- * using {@link AudioManager#setMode(int)} and change it back when none of
- * the AudioGroups is in use.</p>
- *
- * @see AudioStream
- */
-public class AudioGroup {
- /**
- * This mode is similar to {@link #MODE_NORMAL} except the speaker and
- * the microphone are both disabled.
- */
- public static final int MODE_ON_HOLD = 0;
-
- /**
- * This mode is similar to {@link #MODE_NORMAL} except the microphone is
- * disabled.
- */
- public static final int MODE_MUTED = 1;
-
- /**
- * This mode indicates that the speaker, the microphone, and all
- * {@link AudioStream}s in the group are enabled. First, the packets
- * received from the streams are decoded and mixed with the audio recorded
- * from the microphone. Then, the results are played back to the speaker,
- * encoded and sent back to each stream.
- */
- public static final int MODE_NORMAL = 2;
-
- /**
- * This mode is similar to {@link #MODE_NORMAL} except the echo suppression
- * is enabled. It should be only used when the speaker phone is on.
- */
- public static final int MODE_ECHO_SUPPRESSION = 3;
-
- private static final int MODE_LAST = 3;
-
- private final Map<AudioStream, Integer> mStreams;
- private int mMode = MODE_ON_HOLD;
-
- private int mNative;
- static {
- System.loadLibrary("rtp_jni");
- }
-
- /**
- * Creates an empty AudioGroup.
- */
- public AudioGroup() {
- mStreams = new HashMap<AudioStream, Integer>();
- }
-
- /**
- * Returns the {@link AudioStream}s in this group.
- */
- public AudioStream[] getStreams() {
- synchronized (this) {
- return mStreams.keySet().toArray(new AudioStream[mStreams.size()]);
- }
- }
-
- /**
- * Returns the current mode.
- */
- public int getMode() {
- return mMode;
- }
-
- /**
- * Changes the current mode. It must be one of {@link #MODE_ON_HOLD},
- * {@link #MODE_MUTED}, {@link #MODE_NORMAL}, and
- * {@link #MODE_ECHO_SUPPRESSION}.
- *
- * @param mode The mode to change to.
- * @throws IllegalArgumentException if the mode is invalid.
- */
- public void setMode(int mode) {
- if (mode < 0 || mode > MODE_LAST) {
- throw new IllegalArgumentException("Invalid mode");
- }
- synchronized (this) {
- nativeSetMode(mode);
- mMode = mode;
- }
- }
-
- private native void nativeSetMode(int mode);
-
- // Package-private method used by AudioStream.join().
- synchronized void add(AudioStream stream) {
- if (!mStreams.containsKey(stream)) {
- try {
- AudioCodec codec = stream.getCodec();
- String codecSpec = String.format(Locale.US, "%d %s %s", codec.type,
- codec.rtpmap, codec.fmtp);
- int id = nativeAdd(stream.getMode(), stream.getSocket(),
- stream.getRemoteAddress().getHostAddress(),
- stream.getRemotePort(), codecSpec, stream.getDtmfType());
- mStreams.put(stream, id);
- } catch (NullPointerException e) {
- throw new IllegalStateException(e);
- }
- }
- }
-
- private native int nativeAdd(int mode, int socket, String remoteAddress,
- int remotePort, String codecSpec, int dtmfType);
-
- // Package-private method used by AudioStream.join().
- synchronized void remove(AudioStream stream) {
- Integer id = mStreams.remove(stream);
- if (id != null) {
- nativeRemove(id);
- }
- }
-
- private native void nativeRemove(int id);
-
- /**
- * Sends a DTMF digit to every {@link AudioStream} in this group. Currently
- * only event {@code 0} to {@code 15} are supported.
- *
- * @throws IllegalArgumentException if the event is invalid.
- */
- public void sendDtmf(int event) {
- if (event < 0 || event > 15) {
- throw new IllegalArgumentException("Invalid event");
- }
- synchronized (this) {
- nativeSendDtmf(event);
- }
- }
-
- private native void nativeSendDtmf(int event);
-
- /**
- * Removes every {@link AudioStream} in this group.
- */
- public void clear() {
- for (AudioStream stream : getStreams()) {
- stream.join(null);
- }
- }
-
- @Override
- protected void finalize() throws Throwable {
- nativeRemove(0);
- super.finalize();
- }
-}
diff --git a/voip/java/android/net/rtp/AudioStream.java b/voip/java/android/net/rtp/AudioStream.java
deleted file mode 100644
index 5cd1abc..0000000
--- a/voip/java/android/net/rtp/AudioStream.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.rtp;
-
-import java.net.InetAddress;
-import java.net.SocketException;
-
-/**
- * An AudioStream is a {@link RtpStream} which carrys audio payloads over
- * Real-time Transport Protocol (RTP). Two different classes are developed in
- * order to support various usages such as audio conferencing. An AudioStream
- * represents a remote endpoint which consists of a network mapping and a
- * configured {@link AudioCodec}. On the other side, An {@link AudioGroup}
- * represents a local endpoint which mixes all the AudioStreams and optionally
- * interacts with the speaker and the microphone at the same time. The simplest
- * usage includes one for each endpoints. For other combinations, developers
- * should be aware of the limitations described in {@link AudioGroup}.
- *
- * <p>An AudioStream becomes busy when it joins an AudioGroup. In this case most
- * of the setter methods are disabled. This is designed to ease the task of
- * managing native resources. One can always make an AudioStream leave its
- * AudioGroup by calling {@link #join(AudioGroup)} with {@code null} and put it
- * back after the modification is done.</p>
- *
- * <p class="note">Using this class requires
- * {@link android.Manifest.permission#INTERNET} permission.</p>
- *
- * @see RtpStream
- * @see AudioGroup
- */
-public class AudioStream extends RtpStream {
- private AudioCodec mCodec;
- private int mDtmfType = -1;
- private AudioGroup mGroup;
-
- /**
- * Creates an AudioStream on the given local address. Note that the local
- * port is assigned automatically to conform with RFC 3550.
- *
- * @param address The network address of the local host to bind to.
- * @throws SocketException if the address cannot be bound or a problem
- * occurs during binding.
- */
- public AudioStream(InetAddress address) throws SocketException {
- super(address);
- }
-
- /**
- * Returns {@code true} if the stream has already joined an
- * {@link AudioGroup}.
- */
- @Override
- public final boolean isBusy() {
- return mGroup != null;
- }
-
- /**
- * Returns the joined {@link AudioGroup}.
- */
- public AudioGroup getGroup() {
- return mGroup;
- }
-
- /**
- * Joins an {@link AudioGroup}. Each stream can join only one group at a
- * time. The group can be changed by passing a different one or removed
- * by calling this method with {@code null}.
- *
- * @param group The AudioGroup to join or {@code null} to leave.
- * @throws IllegalStateException if the stream is not properly configured.
- * @see AudioGroup
- */
- public void join(AudioGroup group) {
- synchronized (this) {
- if (mGroup == group) {
- return;
- }
- if (mGroup != null) {
- mGroup.remove(this);
- mGroup = null;
- }
- if (group != null) {
- group.add(this);
- mGroup = group;
- }
- }
- }
-
- /**
- * Returns the {@link AudioCodec}, or {@code null} if it is not set.
- *
- * @see #setCodec(AudioCodec)
- */
- public AudioCodec getCodec() {
- return mCodec;
- }
-
- /**
- * Sets the {@link AudioCodec}.
- *
- * @param codec The AudioCodec to be used.
- * @throws IllegalArgumentException if its type is used by DTMF.
- * @throws IllegalStateException if the stream is busy.
- */
- public void setCodec(AudioCodec codec) {
- if (isBusy()) {
- throw new IllegalStateException("Busy");
- }
- if (codec.type == mDtmfType) {
- throw new IllegalArgumentException("The type is used by DTMF");
- }
- mCodec = codec;
- }
-
- /**
- * Returns the RTP payload type for dual-tone multi-frequency (DTMF) digits,
- * or {@code -1} if it is not enabled.
- *
- * @see #setDtmfType(int)
- */
- public int getDtmfType() {
- return mDtmfType;
- }
-
- /**
- * Sets the RTP payload type for dual-tone multi-frequency (DTMF) digits.
- * The primary usage is to send digits to the remote gateway to perform
- * certain tasks, such as second-stage dialing. According to RFC 2833, the
- * RTP payload type for DTMF is assigned dynamically, so it must be in the
- * range of 96 and 127. One can use {@code -1} to disable DTMF and free up
- * the previous assigned type. This method cannot be called when the stream
- * already joined an {@link AudioGroup}.
- *
- * @param type The RTP payload type to be used or {@code -1} to disable it.
- * @throws IllegalArgumentException if the type is invalid or used by codec.
- * @throws IllegalStateException if the stream is busy.
- * @see AudioGroup#sendDtmf(int)
- */
- public void setDtmfType(int type) {
- if (isBusy()) {
- throw new IllegalStateException("Busy");
- }
- if (type != -1) {
- if (type < 96 || type > 127) {
- throw new IllegalArgumentException("Invalid type");
- }
- if (mCodec != null && type == mCodec.type) {
- throw new IllegalArgumentException("The type is used by codec");
- }
- }
- mDtmfType = type;
- }
-}
diff --git a/voip/java/android/net/rtp/RtpStream.java b/voip/java/android/net/rtp/RtpStream.java
deleted file mode 100644
index b9d75cd..0000000
--- a/voip/java/android/net/rtp/RtpStream.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.rtp;
-
-import java.net.InetAddress;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.SocketException;
-
-/**
- * RtpStream represents the base class of streams which send and receive network
- * packets with media payloads over Real-time Transport Protocol (RTP).
- *
- * <p class="note">Using this class requires
- * {@link android.Manifest.permission#INTERNET} permission.</p>
- */
-public class RtpStream {
- /**
- * This mode indicates that the stream sends and receives packets at the
- * same time. This is the initial mode for new streams.
- */
- public static final int MODE_NORMAL = 0;
-
- /**
- * This mode indicates that the stream only sends packets.
- */
- public static final int MODE_SEND_ONLY = 1;
-
- /**
- * This mode indicates that the stream only receives packets.
- */
- public static final int MODE_RECEIVE_ONLY = 2;
-
- private static final int MODE_LAST = 2;
-
- private final InetAddress mLocalAddress;
- private final int mLocalPort;
-
- private InetAddress mRemoteAddress;
- private int mRemotePort = -1;
- private int mMode = MODE_NORMAL;
-
- private int mSocket = -1;
- static {
- System.loadLibrary("rtp_jni");
- }
-
- /**
- * Creates a RtpStream on the given local address. Note that the local
- * port is assigned automatically to conform with RFC 3550.
- *
- * @param address The network address of the local host to bind to.
- * @throws SocketException if the address cannot be bound or a problem
- * occurs during binding.
- */
- RtpStream(InetAddress address) throws SocketException {
- mLocalPort = create(address.getHostAddress());
- mLocalAddress = address;
- }
-
- private native int create(String address) throws SocketException;
-
- /**
- * Returns the network address of the local host.
- */
- public InetAddress getLocalAddress() {
- return mLocalAddress;
- }
-
- /**
- * Returns the network port of the local host.
- */
- public int getLocalPort() {
- return mLocalPort;
- }
-
- /**
- * Returns the network address of the remote host or {@code null} if the
- * stream is not associated.
- */
- public InetAddress getRemoteAddress() {
- return mRemoteAddress;
- }
-
- /**
- * Returns the network port of the remote host or {@code -1} if the stream
- * is not associated.
- */
- public int getRemotePort() {
- return mRemotePort;
- }
-
- /**
- * Returns {@code true} if the stream is busy. In this case most of the
- * setter methods are disabled. This method is intended to be overridden
- * by subclasses.
- */
- public boolean isBusy() {
- return false;
- }
-
- /**
- * Returns the current mode.
- */
- public int getMode() {
- return mMode;
- }
-
- /**
- * Changes the current mode. It must be one of {@link #MODE_NORMAL},
- * {@link #MODE_SEND_ONLY}, and {@link #MODE_RECEIVE_ONLY}.
- *
- * @param mode The mode to change to.
- * @throws IllegalArgumentException if the mode is invalid.
- * @throws IllegalStateException if the stream is busy.
- * @see #isBusy()
- */
- public void setMode(int mode) {
- if (isBusy()) {
- throw new IllegalStateException("Busy");
- }
- if (mode < 0 || mode > MODE_LAST) {
- throw new IllegalArgumentException("Invalid mode");
- }
- mMode = mode;
- }
-
- /**
- * Associates with a remote host. This defines the destination of the
- * outgoing packets.
- *
- * @param address The network address of the remote host.
- * @param port The network port of the remote host.
- * @throws IllegalArgumentException if the address is not supported or the
- * port is invalid.
- * @throws IllegalStateException if the stream is busy.
- * @see #isBusy()
- */
- public void associate(InetAddress address, int port) {
- if (isBusy()) {
- throw new IllegalStateException("Busy");
- }
- if (!(address instanceof Inet4Address && mLocalAddress instanceof Inet4Address) &&
- !(address instanceof Inet6Address && mLocalAddress instanceof Inet6Address)) {
- throw new IllegalArgumentException("Unsupported address");
- }
- if (port < 0 || port > 65535) {
- throw new IllegalArgumentException("Invalid port");
- }
- mRemoteAddress = address;
- mRemotePort = port;
- }
-
- int getSocket() {
- return mSocket;
- }
-
- /**
- * Releases allocated resources. The stream becomes inoperable after calling
- * this method.
- *
- * @throws IllegalStateException if the stream is busy.
- * @see #isBusy()
- */
- public void release() {
- synchronized (this) {
- if (isBusy()) {
- throw new IllegalStateException("Busy");
- }
- close();
- }
- }
-
- private native void close();
-
- @Override
- protected void finalize() throws Throwable {
- close();
- super.finalize();
- }
-}
diff --git a/voip/java/android/net/rtp/package.html b/voip/java/android/net/rtp/package.html
deleted file mode 100644
index 4506b09..0000000
--- a/voip/java/android/net/rtp/package.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<html>
-<body>
-<p>Provides APIs for RTP (Real-time Transport Protocol), allowing applications to manage on-demand
-or interactive data streaming. In particular, apps that provide VOIP, push-to-talk, conferencing,
-and audio streaming can use these APIs to initiate sessions and transmit or receive data streams
-over any available network.</p>
-
-<p>To support audio conferencing and similar usages, you need to instantiate two classes as
-endpoints for the stream:</p>
-
-<ul>
-<li>{@link android.net.rtp.AudioStream} specifies a remote endpoint and consists of network mapping
-and a configured {@link android.net.rtp.AudioCodec}.</li>
-
-<li>{@link android.net.rtp.AudioGroup} represents the local endpoint for one or more {@link
-android.net.rtp.AudioStream}s. The {@link android.net.rtp.AudioGroup} mixes all the {@link
-android.net.rtp.AudioStream}s and optionally interacts with the device speaker and the microphone at
-the same time.</li>
-</ul>
-
-<p>The simplest usage involves a single remote endpoint and local endpoint. For more complex usages,
-refer to the limitations described for {@link android.net.rtp.AudioGroup}.</p>
-
-<p class="note"><strong>Note:</strong> To use the RTP APIs, you must request the {@link
-android.Manifest.permission#INTERNET} and {@link
-android.Manifest.permission#RECORD_AUDIO} permissions in your manifest file.</p>
-</body>
-</html>
\ No newline at end of file
diff --git a/voip/java/android/net/sip/ISipService.aidl b/voip/java/android/net/sip/ISipService.aidl
deleted file mode 100644
index 3250bf9..0000000
--- a/voip/java/android/net/sip/ISipService.aidl
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-import android.app.PendingIntent;
-import android.net.sip.ISipSession;
-import android.net.sip.ISipSessionListener;
-import android.net.sip.SipProfile;
-
-/**
- * {@hide}
- */
-interface ISipService {
- void open(in SipProfile localProfile);
- void open3(in SipProfile localProfile,
- in PendingIntent incomingCallPendingIntent,
- in ISipSessionListener listener);
- void close(in String localProfileUri);
- boolean isOpened(String localProfileUri);
- boolean isRegistered(String localProfileUri);
- void setRegistrationListener(String localProfileUri,
- ISipSessionListener listener);
-
- ISipSession createSession(in SipProfile localProfile,
- in ISipSessionListener listener);
- ISipSession getPendingSession(String callId);
-
- SipProfile[] getListOfProfiles();
-}
diff --git a/voip/java/android/net/sip/ISipSession.aidl b/voip/java/android/net/sip/ISipSession.aidl
deleted file mode 100644
index 2d515db..0000000
--- a/voip/java/android/net/sip/ISipSession.aidl
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-import android.net.sip.ISipSessionListener;
-import android.net.sip.SipProfile;
-
-/**
- * A SIP session that is associated with a SIP dialog or a transaction that is
- * not within a dialog.
- * @hide
- */
-interface ISipSession {
- /**
- * Gets the IP address of the local host on which this SIP session runs.
- *
- * @return the IP address of the local host
- */
- String getLocalIp();
-
- /**
- * Gets the SIP profile that this session is associated with.
- *
- * @return the SIP profile that this session is associated with
- */
- SipProfile getLocalProfile();
-
- /**
- * Gets the SIP profile that this session is connected to. Only available
- * when the session is associated with a SIP dialog.
- *
- * @return the SIP profile that this session is connected to
- */
- SipProfile getPeerProfile();
-
- /**
- * Gets the session state. The value returned must be one of the states in
- * {@link SipSessionState}.
- *
- * @return the session state
- */
- int getState();
-
- /**
- * Checks if the session is in a call.
- *
- * @return true if the session is in a call
- */
- boolean isInCall();
-
- /**
- * Gets the call ID of the session.
- *
- * @return the call ID
- */
- String getCallId();
-
-
- /**
- * Sets the listener to listen to the session events. A {@link ISipSession}
- * can only hold one listener at a time. Subsequent calls to this method
- * override the previous listener.
- *
- * @param listener to listen to the session events of this object
- */
- void setListener(in ISipSessionListener listener);
-
-
- /**
- * Performs registration to the server specified by the associated local
- * profile. The session listener is called back upon success or failure of
- * registration. The method is only valid to call when the session state is
- * in {@link SipSessionState#READY_TO_CALL}.
- *
- * @param duration duration in second before the registration expires
- * @see ISipSessionListener
- */
- void register(int duration);
-
- /**
- * Performs unregistration to the server specified by the associated local
- * profile. Unregistration is technically the same as registration with zero
- * expiration duration. The session listener is called back upon success or
- * failure of unregistration. The method is only valid to call when the
- * session state is in {@link SipSessionState#READY_TO_CALL}.
- *
- * @see ISipSessionListener
- */
- void unregister();
-
- /**
- * Initiates a call to the specified profile. The session listener is called
- * back upon defined session events. The method is only valid to call when
- * the session state is in {@link SipSessionState#READY_TO_CALL}.
- *
- * @param callee the SIP profile to make the call to
- * @param sessionDescription the session description of this call
- * @param timeout the session will be timed out if the call is not
- * established within {@code timeout} seconds
- * @see ISipSessionListener
- */
- void makeCall(in SipProfile callee, String sessionDescription, int timeout);
-
- /**
- * Answers an incoming call with the specified session description. The
- * method is only valid to call when the session state is in
- * {@link SipSessionState#INCOMING_CALL}.
- *
- * @param sessionDescription the session description to answer this call
- * @param timeout the session will be timed out if the call is not
- * established within {@code timeout} seconds
- */
- void answerCall(String sessionDescription, int timeout);
-
- /**
- * Ends an established call, terminates an outgoing call or rejects an
- * incoming call. The method is only valid to call when the session state is
- * in {@link SipSessionState#IN_CALL},
- * {@link SipSessionState#INCOMING_CALL},
- * {@link SipSessionState#OUTGOING_CALL} or
- * {@link SipSessionState#OUTGOING_CALL_RING_BACK}.
- */
- void endCall();
-
- /**
- * Changes the session description during a call. The method is only valid
- * to call when the session state is in {@link SipSessionState#IN_CALL}.
- *
- * @param sessionDescription the new session description
- * @param timeout the session will be timed out if the call is not
- * established within {@code timeout} seconds
- */
- void changeCall(String sessionDescription, int timeout);
-}
diff --git a/voip/java/android/net/sip/ISipSessionListener.aidl b/voip/java/android/net/sip/ISipSessionListener.aidl
deleted file mode 100644
index 690700c..0000000
--- a/voip/java/android/net/sip/ISipSessionListener.aidl
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-import android.net.sip.ISipSession;
-import android.net.sip.SipProfile;
-
-/**
- * Listener class to listen to SIP session events.
- * @hide
- */
-interface ISipSessionListener {
- /**
- * Called when an INVITE request is sent to initiate a new call.
- *
- * @param session the session object that carries out the transaction
- */
- void onCalling(in ISipSession session);
-
- /**
- * Called when an INVITE request is received.
- *
- * @param session the session object that carries out the transaction
- * @param caller the SIP profile of the caller
- * @param sessionDescription the caller's session description
- */
- void onRinging(in ISipSession session, in SipProfile caller,
- String sessionDescription);
-
- /**
- * Called when a RINGING response is received for the INVITE request sent
- *
- * @param session the session object that carries out the transaction
- */
- void onRingingBack(in ISipSession session);
-
- /**
- * Called when the session is established.
- *
- * @param session the session object that is associated with the dialog
- * @param sessionDescription the peer's session description
- */
- void onCallEstablished(in ISipSession session,
- String sessionDescription);
-
- /**
- * Called when the session is terminated.
- *
- * @param session the session object that is associated with the dialog
- */
- void onCallEnded(in ISipSession session);
-
- /**
- * Called when the peer is busy during session initialization.
- *
- * @param session the session object that carries out the transaction
- */
- void onCallBusy(in ISipSession session);
-
- /**
- * Called when the call is being transferred to a new one.
- *
- * @param newSession the new session that the call will be transferred to
- * @param sessionDescription the new peer's session description
- */
- void onCallTransferring(in ISipSession newSession, String sessionDescription);
-
- /**
- * Called when an error occurs during session initialization and
- * termination.
- *
- * @param session the session object that carries out the transaction
- * @param errorCode error code defined in {@link SipErrorCode}
- * @param errorMessage error message
- */
- void onError(in ISipSession session, int errorCode, String errorMessage);
-
- /**
- * Called when an error occurs during session modification negotiation.
- *
- * @param session the session object that carries out the transaction
- * @param errorCode error code defined in {@link SipErrorCode}
- * @param errorMessage error message
- */
- void onCallChangeFailed(in ISipSession session, int errorCode,
- String errorMessage);
-
- /**
- * Called when a registration request is sent.
- *
- * @param session the session object that carries out the transaction
- */
- void onRegistering(in ISipSession session);
-
- /**
- * Called when registration is successfully done.
- *
- * @param session the session object that carries out the transaction
- * @param duration duration in second before the registration expires
- */
- void onRegistrationDone(in ISipSession session, int duration);
-
- /**
- * Called when the registration fails.
- *
- * @param session the session object that carries out the transaction
- * @param errorCode error code defined in {@link SipErrorCode}
- * @param errorMessage error message
- */
- void onRegistrationFailed(in ISipSession session, int errorCode,
- String errorMessage);
-
- /**
- * Called when the registration gets timed out.
- *
- * @param session the session object that carries out the transaction
- */
- void onRegistrationTimeout(in ISipSession session);
-}
diff --git a/voip/java/android/net/sip/SimpleSessionDescription.java b/voip/java/android/net/sip/SimpleSessionDescription.java
deleted file mode 100644
index 9fcd21d..0000000
--- a/voip/java/android/net/sip/SimpleSessionDescription.java
+++ /dev/null
@@ -1,613 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Locale;
-
-/**
- * An object used to manipulate messages of Session Description Protocol (SDP).
- * It is mainly designed for the uses of Session Initiation Protocol (SIP).
- * Therefore, it only handles connection addresses ("c="), bandwidth limits,
- * ("b="), encryption keys ("k="), and attribute fields ("a="). Currently this
- * implementation does not support multicast sessions.
- *
- * <p>Here is an example code to create a session description.</p>
- * <pre>
- * SimpleSessionDescription description = new SimpleSessionDescription(
- * System.currentTimeMillis(), "1.2.3.4");
- * Media media = description.newMedia("audio", 56789, 1, "RTP/AVP");
- * media.setRtpPayload(0, "PCMU/8000", null);
- * media.setRtpPayload(8, "PCMA/8000", null);
- * media.setRtpPayload(127, "telephone-event/8000", "0-15");
- * media.setAttribute("sendrecv", "");
- * </pre>
- * <p>Invoking <code>description.encode()</code> will produce a result like the
- * one below.</p>
- * <pre>
- * v=0
- * o=- 1284970442706 1284970442709 IN IP4 1.2.3.4
- * s=-
- * c=IN IP4 1.2.3.4
- * t=0 0
- * m=audio 56789 RTP/AVP 0 8 127
- * a=rtpmap:0 PCMU/8000
- * a=rtpmap:8 PCMA/8000
- * a=rtpmap:127 telephone-event/8000
- * a=fmtp:127 0-15
- * a=sendrecv
- * </pre>
- * @hide
- */
-public class SimpleSessionDescription {
- private final Fields mFields = new Fields("voscbtka");
- private final ArrayList<Media> mMedia = new ArrayList<Media>();
-
- /**
- * Creates a minimal session description from the given session ID and
- * unicast address. The address is used in the origin field ("o=") and the
- * connection field ("c="). See {@link SimpleSessionDescription} for an
- * example of its usage.
- */
- public SimpleSessionDescription(long sessionId, String address) {
- address = (address.indexOf(':') < 0 ? "IN IP4 " : "IN IP6 ") + address;
- mFields.parse("v=0");
- mFields.parse(String.format(Locale.US, "o=- %d %d %s", sessionId,
- System.currentTimeMillis(), address));
- mFields.parse("s=-");
- mFields.parse("t=0 0");
- mFields.parse("c=" + address);
- }
-
- /**
- * Creates a session description from the given message.
- *
- * @throws IllegalArgumentException if message is invalid.
- */
- public SimpleSessionDescription(String message) {
- String[] lines = message.trim().replaceAll(" +", " ").split("[\r\n]+");
- Fields fields = mFields;
-
- for (String line : lines) {
- try {
- if (line.charAt(1) != '=') {
- throw new IllegalArgumentException();
- }
- if (line.charAt(0) == 'm') {
- String[] parts = line.substring(2).split(" ", 4);
- String[] ports = parts[1].split("/", 2);
- Media media = newMedia(parts[0], Integer.parseInt(ports[0]),
- (ports.length < 2) ? 1 : Integer.parseInt(ports[1]),
- parts[2]);
- for (String format : parts[3].split(" ")) {
- media.setFormat(format, null);
- }
- fields = media;
- } else {
- fields.parse(line);
- }
- } catch (Exception e) {
- throw new IllegalArgumentException("Invalid SDP: " + line);
- }
- }
- }
-
- /**
- * Creates a new media description in this session description.
- *
- * @param type The media type, e.g. {@code "audio"}.
- * @param port The first transport port used by this media.
- * @param portCount The number of contiguous ports used by this media.
- * @param protocol The transport protocol, e.g. {@code "RTP/AVP"}.
- */
- public Media newMedia(String type, int port, int portCount,
- String protocol) {
- Media media = new Media(type, port, portCount, protocol);
- mMedia.add(media);
- return media;
- }
-
- /**
- * Returns all the media descriptions in this session description.
- */
- public Media[] getMedia() {
- return mMedia.toArray(new Media[mMedia.size()]);
- }
-
- /**
- * Encodes the session description and all its media descriptions in a
- * string. Note that the result might be incomplete if a required field
- * has never been added before.
- */
- public String encode() {
- StringBuilder buffer = new StringBuilder();
- mFields.write(buffer);
- for (Media media : mMedia) {
- media.write(buffer);
- }
- return buffer.toString();
- }
-
- /**
- * Returns the connection address or {@code null} if it is not present.
- */
- public String getAddress() {
- return mFields.getAddress();
- }
-
- /**
- * Sets the connection address. The field will be removed if the address
- * is {@code null}.
- */
- public void setAddress(String address) {
- mFields.setAddress(address);
- }
-
- /**
- * Returns the encryption method or {@code null} if it is not present.
- */
- public String getEncryptionMethod() {
- return mFields.getEncryptionMethod();
- }
-
- /**
- * Returns the encryption key or {@code null} if it is not present.
- */
- public String getEncryptionKey() {
- return mFields.getEncryptionKey();
- }
-
- /**
- * Sets the encryption method and the encryption key. The field will be
- * removed if the method is {@code null}.
- */
- public void setEncryption(String method, String key) {
- mFields.setEncryption(method, key);
- }
-
- /**
- * Returns the types of the bandwidth limits.
- */
- public String[] getBandwidthTypes() {
- return mFields.getBandwidthTypes();
- }
-
- /**
- * Returns the bandwidth limit of the given type or {@code -1} if it is not
- * present.
- */
- public int getBandwidth(String type) {
- return mFields.getBandwidth(type);
- }
-
- /**
- * Sets the bandwith limit for the given type. The field will be removed if
- * the value is negative.
- */
- public void setBandwidth(String type, int value) {
- mFields.setBandwidth(type, value);
- }
-
- /**
- * Returns the names of all the attributes.
- */
- public String[] getAttributeNames() {
- return mFields.getAttributeNames();
- }
-
- /**
- * Returns the attribute of the given name or {@code null} if it is not
- * present.
- */
- public String getAttribute(String name) {
- return mFields.getAttribute(name);
- }
-
- /**
- * Sets the attribute for the given name. The field will be removed if
- * the value is {@code null}. To set a binary attribute, use an empty
- * string as the value.
- */
- public void setAttribute(String name, String value) {
- mFields.setAttribute(name, value);
- }
-
- /**
- * This class represents a media description of a session description. It
- * can only be created by {@link SimpleSessionDescription#newMedia}. Since
- * the syntax is more restricted for RTP based protocols, two sets of access
- * methods are implemented. See {@link SimpleSessionDescription} for an
- * example of its usage.
- */
- public static class Media extends Fields {
- private final String mType;
- private final int mPort;
- private final int mPortCount;
- private final String mProtocol;
- private ArrayList<String> mFormats = new ArrayList<String>();
-
- private Media(String type, int port, int portCount, String protocol) {
- super("icbka");
- mType = type;
- mPort = port;
- mPortCount = portCount;
- mProtocol = protocol;
- }
-
- /**
- * Returns the media type.
- */
- public String getType() {
- return mType;
- }
-
- /**
- * Returns the first transport port used by this media.
- */
- public int getPort() {
- return mPort;
- }
-
- /**
- * Returns the number of contiguous ports used by this media.
- */
- public int getPortCount() {
- return mPortCount;
- }
-
- /**
- * Returns the transport protocol.
- */
- public String getProtocol() {
- return mProtocol;
- }
-
- /**
- * Returns the media formats.
- */
- public String[] getFormats() {
- return mFormats.toArray(new String[mFormats.size()]);
- }
-
- /**
- * Returns the {@code fmtp} attribute of the given format or
- * {@code null} if it is not present.
- */
- public String getFmtp(String format) {
- return super.get("a=fmtp:" + format, ' ');
- }
-
- /**
- * Sets a format and its {@code fmtp} attribute. If the attribute is
- * {@code null}, the corresponding field will be removed.
- */
- public void setFormat(String format, String fmtp) {
- mFormats.remove(format);
- mFormats.add(format);
- super.set("a=rtpmap:" + format, ' ', null);
- super.set("a=fmtp:" + format, ' ', fmtp);
- }
-
- /**
- * Removes a format and its {@code fmtp} attribute.
- */
- public void removeFormat(String format) {
- mFormats.remove(format);
- super.set("a=rtpmap:" + format, ' ', null);
- super.set("a=fmtp:" + format, ' ', null);
- }
-
- /**
- * Returns the RTP payload types.
- */
- public int[] getRtpPayloadTypes() {
- int[] types = new int[mFormats.size()];
- int length = 0;
- for (String format : mFormats) {
- try {
- types[length] = Integer.parseInt(format);
- ++length;
- } catch (NumberFormatException e) { }
- }
- return Arrays.copyOf(types, length);
- }
-
- /**
- * Returns the {@code rtpmap} attribute of the given RTP payload type
- * or {@code null} if it is not present.
- */
- public String getRtpmap(int type) {
- return super.get("a=rtpmap:" + type, ' ');
- }
-
- /**
- * Returns the {@code fmtp} attribute of the given RTP payload type or
- * {@code null} if it is not present.
- */
- public String getFmtp(int type) {
- return super.get("a=fmtp:" + type, ' ');
- }
-
- /**
- * Sets a RTP payload type and its {@code rtpmap} and {@code fmtp}
- * attributes. If any of the attributes is {@code null}, the
- * corresponding field will be removed. See
- * {@link SimpleSessionDescription} for an example of its usage.
- */
- public void setRtpPayload(int type, String rtpmap, String fmtp) {
- String format = String.valueOf(type);
- mFormats.remove(format);
- mFormats.add(format);
- super.set("a=rtpmap:" + format, ' ', rtpmap);
- super.set("a=fmtp:" + format, ' ', fmtp);
- }
-
- /**
- * Removes a RTP payload and its {@code rtpmap} and {@code fmtp}
- * attributes.
- */
- public void removeRtpPayload(int type) {
- removeFormat(String.valueOf(type));
- }
-
- private void write(StringBuilder buffer) {
- buffer.append("m=").append(mType).append(' ').append(mPort);
- if (mPortCount != 1) {
- buffer.append('/').append(mPortCount);
- }
- buffer.append(' ').append(mProtocol);
- for (String format : mFormats) {
- buffer.append(' ').append(format);
- }
- buffer.append("\r\n");
- super.write(buffer);
- }
- }
-
- /**
- * This class acts as a set of fields, and the size of the set is expected
- * to be small. Therefore, it uses a simple list instead of maps. Each field
- * has three parts: a key, a delimiter, and a value. Delimiters are special
- * because they are not included in binary attributes. As a result, the
- * private methods, which are the building blocks of this class, all take
- * the delimiter as an argument.
- */
- private static class Fields {
- private final String mOrder;
- private final ArrayList<String> mLines = new ArrayList<String>();
-
- Fields(String order) {
- mOrder = order;
- }
-
- /**
- * Returns the connection address or {@code null} if it is not present.
- */
- public String getAddress() {
- String address = get("c", '=');
- if (address == null) {
- return null;
- }
- String[] parts = address.split(" ");
- if (parts.length != 3) {
- return null;
- }
- int slash = parts[2].indexOf('/');
- return (slash < 0) ? parts[2] : parts[2].substring(0, slash);
- }
-
- /**
- * Sets the connection address. The field will be removed if the address
- * is {@code null}.
- */
- public void setAddress(String address) {
- if (address != null) {
- address = (address.indexOf(':') < 0 ? "IN IP4 " : "IN IP6 ") +
- address;
- }
- set("c", '=', address);
- }
-
- /**
- * Returns the encryption method or {@code null} if it is not present.
- */
- public String getEncryptionMethod() {
- String encryption = get("k", '=');
- if (encryption == null) {
- return null;
- }
- int colon = encryption.indexOf(':');
- return (colon == -1) ? encryption : encryption.substring(0, colon);
- }
-
- /**
- * Returns the encryption key or {@code null} if it is not present.
- */
- public String getEncryptionKey() {
- String encryption = get("k", '=');
- if (encryption == null) {
- return null;
- }
- int colon = encryption.indexOf(':');
- return (colon == -1) ? null : encryption.substring(0, colon + 1);
- }
-
- /**
- * Sets the encryption method and the encryption key. The field will be
- * removed if the method is {@code null}.
- */
- public void setEncryption(String method, String key) {
- set("k", '=', (method == null || key == null) ?
- method : method + ':' + key);
- }
-
- /**
- * Returns the types of the bandwidth limits.
- */
- public String[] getBandwidthTypes() {
- return cut("b=", ':');
- }
-
- /**
- * Returns the bandwidth limit of the given type or {@code -1} if it is
- * not present.
- */
- public int getBandwidth(String type) {
- String value = get("b=" + type, ':');
- if (value != null) {
- try {
- return Integer.parseInt(value);
- } catch (NumberFormatException e) { }
- setBandwidth(type, -1);
- }
- return -1;
- }
-
- /**
- * Sets the bandwith limit for the given type. The field will be removed
- * if the value is negative.
- */
- public void setBandwidth(String type, int value) {
- set("b=" + type, ':', (value < 0) ? null : String.valueOf(value));
- }
-
- /**
- * Returns the names of all the attributes.
- */
- public String[] getAttributeNames() {
- return cut("a=", ':');
- }
-
- /**
- * Returns the attribute of the given name or {@code null} if it is not
- * present.
- */
- public String getAttribute(String name) {
- return get("a=" + name, ':');
- }
-
- /**
- * Sets the attribute for the given name. The field will be removed if
- * the value is {@code null}. To set a binary attribute, use an empty
- * string as the value.
- */
- public void setAttribute(String name, String value) {
- set("a=" + name, ':', value);
- }
-
- private void write(StringBuilder buffer) {
- for (int i = 0; i < mOrder.length(); ++i) {
- char type = mOrder.charAt(i);
- for (String line : mLines) {
- if (line.charAt(0) == type) {
- buffer.append(line).append("\r\n");
- }
- }
- }
- }
-
- /**
- * Invokes {@link #set} after splitting the line into three parts.
- */
- private void parse(String line) {
- char type = line.charAt(0);
- if (mOrder.indexOf(type) == -1) {
- return;
- }
- char delimiter = '=';
- if (line.startsWith("a=rtpmap:") || line.startsWith("a=fmtp:")) {
- delimiter = ' ';
- } else if (type == 'b' || type == 'a') {
- delimiter = ':';
- }
- int i = line.indexOf(delimiter);
- if (i == -1) {
- set(line, delimiter, "");
- } else {
- set(line.substring(0, i), delimiter, line.substring(i + 1));
- }
- }
-
- /**
- * Finds the key with the given prefix and returns its suffix.
- */
- private String[] cut(String prefix, char delimiter) {
- String[] names = new String[mLines.size()];
- int length = 0;
- for (String line : mLines) {
- if (line.startsWith(prefix)) {
- int i = line.indexOf(delimiter);
- if (i == -1) {
- i = line.length();
- }
- names[length] = line.substring(prefix.length(), i);
- ++length;
- }
- }
- return Arrays.copyOf(names, length);
- }
-
- /**
- * Returns the index of the key.
- */
- private int find(String key, char delimiter) {
- int length = key.length();
- for (int i = mLines.size() - 1; i >= 0; --i) {
- String line = mLines.get(i);
- if (line.startsWith(key) && (line.length() == length ||
- line.charAt(length) == delimiter)) {
- return i;
- }
- }
- return -1;
- }
-
- /**
- * Sets the key with the value or removes the key if the value is
- * {@code null}.
- */
- private void set(String key, char delimiter, String value) {
- int index = find(key, delimiter);
- if (value != null) {
- if (value.length() != 0) {
- key = key + delimiter + value;
- }
- if (index == -1) {
- mLines.add(key);
- } else {
- mLines.set(index, key);
- }
- } else if (index != -1) {
- mLines.remove(index);
- }
- }
-
- /**
- * Returns the value of the key.
- */
- private String get(String key, char delimiter) {
- int index = find(key, delimiter);
- if (index == -1) {
- return null;
- }
- String line = mLines.get(index);
- int length = key.length();
- return (line.length() == length) ? "" : line.substring(length + 1);
- }
- }
-}
diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java
deleted file mode 100644
index ea943e9..0000000
--- a/voip/java/android/net/sip/SipAudioCall.java
+++ /dev/null
@@ -1,1143 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-import android.content.Context;
-import android.media.AudioManager;
-import android.net.rtp.AudioCodec;
-import android.net.rtp.AudioGroup;
-import android.net.rtp.AudioStream;
-import android.net.rtp.RtpStream;
-import android.net.sip.SimpleSessionDescription.Media;
-import android.net.wifi.WifiManager;
-import android.os.Message;
-import android.telephony.Rlog;
-import android.text.TextUtils;
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-/**
- * Handles an Internet audio call over SIP. You can instantiate this class with {@link SipManager},
- * using {@link SipManager#makeAudioCall makeAudioCall()} and {@link SipManager#takeAudioCall
- * takeAudioCall()}.
- *
- * <p class="note"><strong>Note:</strong> Using this class require the
- * {@link android.Manifest.permission#INTERNET} and
- * {@link android.Manifest.permission#USE_SIP} permissions. In addition, {@link
- * #startAudio} requires the
- * {@link android.Manifest.permission#RECORD_AUDIO},
- * {@link android.Manifest.permission#ACCESS_WIFI_STATE}, and
- * {@link android.Manifest.permission#WAKE_LOCK} permissions; and {@link #setSpeakerMode
- * setSpeakerMode()} requires the
- * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS} permission.</p>
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For more information about using SIP, read the
- * <a href="{@docRoot}guide/topics/network/sip.html">Session Initiation Protocol</a>
- * developer guide.</p>
- * </div>
- */
-public class SipAudioCall {
- private static final String LOG_TAG = SipAudioCall.class.getSimpleName();
- private static final boolean DBG = true;
- private static final boolean RELEASE_SOCKET = true;
- private static final boolean DONT_RELEASE_SOCKET = false;
- private static final int SESSION_TIMEOUT = 5; // in seconds
- private static final int TRANSFER_TIMEOUT = 15; // in seconds
-
- /** Listener for events relating to a SIP call, such as when a call is being
- * recieved ("on ringing") or a call is outgoing ("on calling").
- * <p>Many of these events are also received by {@link SipSession.Listener}.</p>
- */
- public static class Listener {
- /**
- * Called when the call object is ready to make another call.
- * The default implementation calls {@link #onChanged}.
- *
- * @param call the call object that is ready to make another call
- */
- public void onReadyToCall(SipAudioCall call) {
- onChanged(call);
- }
-
- /**
- * Called when a request is sent out to initiate a new call.
- * The default implementation calls {@link #onChanged}.
- *
- * @param call the call object that carries out the audio call
- */
- public void onCalling(SipAudioCall call) {
- onChanged(call);
- }
-
- /**
- * Called when a new call comes in.
- * The default implementation calls {@link #onChanged}.
- *
- * @param call the call object that carries out the audio call
- * @param caller the SIP profile of the caller
- */
- public void onRinging(SipAudioCall call, SipProfile caller) {
- onChanged(call);
- }
-
- /**
- * Called when a RINGING response is received for the INVITE request
- * sent. The default implementation calls {@link #onChanged}.
- *
- * @param call the call object that carries out the audio call
- */
- public void onRingingBack(SipAudioCall call) {
- onChanged(call);
- }
-
- /**
- * Called when the session is established.
- * The default implementation calls {@link #onChanged}.
- *
- * @param call the call object that carries out the audio call
- */
- public void onCallEstablished(SipAudioCall call) {
- onChanged(call);
- }
-
- /**
- * Called when the session is terminated.
- * The default implementation calls {@link #onChanged}.
- *
- * @param call the call object that carries out the audio call
- */
- public void onCallEnded(SipAudioCall call) {
- onChanged(call);
- }
-
- /**
- * Called when the peer is busy during session initialization.
- * The default implementation calls {@link #onChanged}.
- *
- * @param call the call object that carries out the audio call
- */
- public void onCallBusy(SipAudioCall call) {
- onChanged(call);
- }
-
- /**
- * Called when the call is on hold.
- * The default implementation calls {@link #onChanged}.
- *
- * @param call the call object that carries out the audio call
- */
- public void onCallHeld(SipAudioCall call) {
- onChanged(call);
- }
-
- /**
- * Called when an error occurs. The default implementation is no op.
- *
- * @param call the call object that carries out the audio call
- * @param errorCode error code of this error
- * @param errorMessage error message
- * @see SipErrorCode
- */
- public void onError(SipAudioCall call, int errorCode,
- String errorMessage) {
- // no-op
- }
-
- /**
- * Called when an event occurs and the corresponding callback is not
- * overridden. The default implementation is no op. Error events are
- * not re-directed to this callback and are handled in {@link #onError}.
- */
- public void onChanged(SipAudioCall call) {
- // no-op
- }
- }
-
- private Context mContext;
- private SipProfile mLocalProfile;
- private SipAudioCall.Listener mListener;
- private SipSession mSipSession;
- private SipSession mTransferringSession;
-
- private long mSessionId = System.currentTimeMillis();
- private String mPeerSd;
-
- private AudioStream mAudioStream;
- private AudioGroup mAudioGroup;
-
- private boolean mInCall = false;
- private boolean mMuted = false;
- private boolean mHold = false;
-
- private WifiManager mWm;
- private WifiManager.WifiLock mWifiHighPerfLock;
-
- private int mErrorCode = SipErrorCode.NO_ERROR;
- private String mErrorMessage;
-
- /**
- * Creates a call object with the local SIP profile.
- * @param context the context for accessing system services such as
- * ringtone, audio, WIFI etc
- */
- public SipAudioCall(Context context, SipProfile localProfile) {
- mContext = context;
- mLocalProfile = localProfile;
- mWm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
- }
-
- /**
- * Sets the listener to listen to the audio call events. The method calls
- * {@link #setListener setListener(listener, false)}.
- *
- * @param listener to listen to the audio call events of this object
- * @see #setListener(Listener, boolean)
- */
- public void setListener(SipAudioCall.Listener listener) {
- setListener(listener, false);
- }
-
- /**
- * Sets the listener to listen to the audio call events. A
- * {@link SipAudioCall} can only hold one listener at a time. Subsequent
- * calls to this method override the previous listener.
- *
- * @param listener to listen to the audio call events of this object
- * @param callbackImmediately set to true if the caller wants to be called
- * back immediately on the current state
- */
- public void setListener(SipAudioCall.Listener listener,
- boolean callbackImmediately) {
- mListener = listener;
- try {
- if ((listener == null) || !callbackImmediately) {
- // do nothing
- } else if (mErrorCode != SipErrorCode.NO_ERROR) {
- listener.onError(this, mErrorCode, mErrorMessage);
- } else if (mInCall) {
- if (mHold) {
- listener.onCallHeld(this);
- } else {
- listener.onCallEstablished(this);
- }
- } else {
- int state = getState();
- switch (state) {
- case SipSession.State.READY_TO_CALL:
- listener.onReadyToCall(this);
- break;
- case SipSession.State.INCOMING_CALL:
- listener.onRinging(this, getPeerProfile());
- break;
- case SipSession.State.OUTGOING_CALL:
- listener.onCalling(this);
- break;
- case SipSession.State.OUTGOING_CALL_RING_BACK:
- listener.onRingingBack(this);
- break;
- }
- }
- } catch (Throwable t) {
- loge("setListener()", t);
- }
- }
-
- /**
- * Checks if the call is established.
- *
- * @return true if the call is established
- */
- public boolean isInCall() {
- synchronized (this) {
- return mInCall;
- }
- }
-
- /**
- * Checks if the call is on hold.
- *
- * @return true if the call is on hold
- */
- public boolean isOnHold() {
- synchronized (this) {
- return mHold;
- }
- }
-
- /**
- * Closes this object. This object is not usable after being closed.
- */
- public void close() {
- close(true);
- }
-
- private synchronized void close(boolean closeRtp) {
- if (closeRtp) stopCall(RELEASE_SOCKET);
-
- mInCall = false;
- mHold = false;
- mSessionId = System.currentTimeMillis();
- mErrorCode = SipErrorCode.NO_ERROR;
- mErrorMessage = null;
-
- if (mSipSession != null) {
- mSipSession.setListener(null);
- mSipSession = null;
- }
- }
-
- /**
- * Gets the local SIP profile.
- *
- * @return the local SIP profile
- */
- public SipProfile getLocalProfile() {
- synchronized (this) {
- return mLocalProfile;
- }
- }
-
- /**
- * Gets the peer's SIP profile.
- *
- * @return the peer's SIP profile
- */
- public SipProfile getPeerProfile() {
- synchronized (this) {
- return (mSipSession == null) ? null : mSipSession.getPeerProfile();
- }
- }
-
- /**
- * Gets the state of the {@link SipSession} that carries this call.
- * The value returned must be one of the states in {@link SipSession.State}.
- *
- * @return the session state
- */
- public int getState() {
- synchronized (this) {
- if (mSipSession == null) return SipSession.State.READY_TO_CALL;
- return mSipSession.getState();
- }
- }
-
-
- /**
- * Gets the {@link SipSession} that carries this call.
- *
- * @return the session object that carries this call
- * @hide
- */
- public SipSession getSipSession() {
- synchronized (this) {
- return mSipSession;
- }
- }
-
- private synchronized void transferToNewSession() {
- if (mTransferringSession == null) return;
- SipSession origin = mSipSession;
- mSipSession = mTransferringSession;
- mTransferringSession = null;
-
- // stop the replaced call.
- if (mAudioStream != null) {
- mAudioStream.join(null);
- } else {
- try {
- mAudioStream = new AudioStream(InetAddress.getByName(
- getLocalIp()));
- } catch (Throwable t) {
- loge("transferToNewSession():", t);
- }
- }
- if (origin != null) origin.endCall();
- startAudio();
- }
-
- private SipSession.Listener createListener() {
- return new SipSession.Listener() {
- @Override
- public void onCalling(SipSession session) {
- if (DBG) log("onCalling: session=" + session);
- Listener listener = mListener;
- if (listener != null) {
- try {
- listener.onCalling(SipAudioCall.this);
- } catch (Throwable t) {
- loge("onCalling():", t);
- }
- }
- }
-
- @Override
- public void onRingingBack(SipSession session) {
- if (DBG) log("onRingingBackk: " + session);
- Listener listener = mListener;
- if (listener != null) {
- try {
- listener.onRingingBack(SipAudioCall.this);
- } catch (Throwable t) {
- loge("onRingingBack():", t);
- }
- }
- }
-
- @Override
- public void onRinging(SipSession session,
- SipProfile peerProfile, String sessionDescription) {
- // this callback is triggered only for reinvite.
- synchronized (SipAudioCall.this) {
- if ((mSipSession == null) || !mInCall
- || !session.getCallId().equals(
- mSipSession.getCallId())) {
- // should not happen
- session.endCall();
- return;
- }
-
- // session changing request
- try {
- String answer = createAnswer(sessionDescription).encode();
- mSipSession.answerCall(answer, SESSION_TIMEOUT);
- } catch (Throwable e) {
- loge("onRinging():", e);
- session.endCall();
- }
- }
- }
-
- @Override
- public void onCallEstablished(SipSession session,
- String sessionDescription) {
- mPeerSd = sessionDescription;
- if (DBG) log("onCallEstablished(): " + mPeerSd);
-
- // TODO: how to notify the UI that the remote party is changed
- if ((mTransferringSession != null)
- && (session == mTransferringSession)) {
- transferToNewSession();
- return;
- }
-
- Listener listener = mListener;
- if (listener != null) {
- try {
- if (mHold) {
- listener.onCallHeld(SipAudioCall.this);
- } else {
- listener.onCallEstablished(SipAudioCall.this);
- }
- } catch (Throwable t) {
- loge("onCallEstablished(): ", t);
- }
- }
- }
-
- @Override
- public void onCallEnded(SipSession session) {
- if (DBG) log("onCallEnded: " + session + " mSipSession:" + mSipSession);
- // reset the trasnferring session if it is the one.
- if (session == mTransferringSession) {
- mTransferringSession = null;
- return;
- }
- // or ignore the event if the original session is being
- // transferred to the new one.
- if ((mTransferringSession != null) ||
- (session != mSipSession)) return;
-
- Listener listener = mListener;
- if (listener != null) {
- try {
- listener.onCallEnded(SipAudioCall.this);
- } catch (Throwable t) {
- loge("onCallEnded(): ", t);
- }
- }
- close();
- }
-
- @Override
- public void onCallBusy(SipSession session) {
- if (DBG) log("onCallBusy: " + session);
- Listener listener = mListener;
- if (listener != null) {
- try {
- listener.onCallBusy(SipAudioCall.this);
- } catch (Throwable t) {
- loge("onCallBusy(): ", t);
- }
- }
- close(false);
- }
-
- @Override
- public void onCallChangeFailed(SipSession session, int errorCode,
- String message) {
- if (DBG) log("onCallChangedFailed: " + message);
- mErrorCode = errorCode;
- mErrorMessage = message;
- Listener listener = mListener;
- if (listener != null) {
- try {
- listener.onError(SipAudioCall.this, mErrorCode,
- message);
- } catch (Throwable t) {
- loge("onCallBusy():", t);
- }
- }
- }
-
- @Override
- public void onError(SipSession session, int errorCode,
- String message) {
- SipAudioCall.this.onError(errorCode, message);
- }
-
- @Override
- public void onRegistering(SipSession session) {
- // irrelevant
- }
-
- @Override
- public void onRegistrationTimeout(SipSession session) {
- // irrelevant
- }
-
- @Override
- public void onRegistrationFailed(SipSession session, int errorCode,
- String message) {
- // irrelevant
- }
-
- @Override
- public void onRegistrationDone(SipSession session, int duration) {
- // irrelevant
- }
-
- @Override
- public void onCallTransferring(SipSession newSession,
- String sessionDescription) {
- if (DBG) log("onCallTransferring: mSipSession="
- + mSipSession + " newSession=" + newSession);
- mTransferringSession = newSession;
- try {
- if (sessionDescription == null) {
- newSession.makeCall(newSession.getPeerProfile(),
- createOffer().encode(), TRANSFER_TIMEOUT);
- } else {
- String answer = createAnswer(sessionDescription).encode();
- newSession.answerCall(answer, SESSION_TIMEOUT);
- }
- } catch (Throwable e) {
- loge("onCallTransferring()", e);
- newSession.endCall();
- }
- }
- };
- }
-
- private void onError(int errorCode, String message) {
- if (DBG) log("onError: "
- + SipErrorCode.toString(errorCode) + ": " + message);
- mErrorCode = errorCode;
- mErrorMessage = message;
- Listener listener = mListener;
- if (listener != null) {
- try {
- listener.onError(this, errorCode, message);
- } catch (Throwable t) {
- loge("onError():", t);
- }
- }
- synchronized (this) {
- if ((errorCode == SipErrorCode.DATA_CONNECTION_LOST)
- || !isInCall()) {
- close(true);
- }
- }
- }
-
- /**
- * Attaches an incoming call to this call object.
- *
- * @param session the session that receives the incoming call
- * @param sessionDescription the session description of the incoming call
- * @throws SipException if the SIP service fails to attach this object to
- * the session or VOIP API is not supported by the device
- * @see SipManager#isVoipSupported
- */
- public void attachCall(SipSession session, String sessionDescription)
- throws SipException {
- if (!SipManager.isVoipSupported(mContext)) {
- throw new SipException("VOIP API is not supported");
- }
-
- synchronized (this) {
- mSipSession = session;
- mPeerSd = sessionDescription;
- if (DBG) log("attachCall(): " + mPeerSd);
- try {
- session.setListener(createListener());
- } catch (Throwable e) {
- loge("attachCall()", e);
- throwSipException(e);
- }
- }
- }
-
- /**
- * Initiates an audio call to the specified profile. The attempt will be
- * timed out if the call is not established within {@code timeout} seconds
- * and {@link Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
- * will be called.
- *
- * @param peerProfile the SIP profile to make the call to
- * @param sipSession the {@link SipSession} for carrying out the call
- * @param timeout the timeout value in seconds. Default value (defined by
- * SIP protocol) is used if {@code timeout} is zero or negative.
- * @see Listener#onError
- * @throws SipException if the SIP service fails to create a session for the
- * call or VOIP API is not supported by the device
- * @see SipManager#isVoipSupported
- */
- public void makeCall(SipProfile peerProfile, SipSession sipSession,
- int timeout) throws SipException {
- if (DBG) log("makeCall: " + peerProfile + " session=" + sipSession + " timeout=" + timeout);
- if (!SipManager.isVoipSupported(mContext)) {
- throw new SipException("VOIP API is not supported");
- }
-
- synchronized (this) {
- mSipSession = sipSession;
- try {
- mAudioStream = new AudioStream(InetAddress.getByName(
- getLocalIp()));
- sipSession.setListener(createListener());
- sipSession.makeCall(peerProfile, createOffer().encode(),
- timeout);
- } catch (IOException e) {
- loge("makeCall:", e);
- throw new SipException("makeCall()", e);
- }
- }
- }
-
- /**
- * Ends a call.
- * @throws SipException if the SIP service fails to end the call
- */
- public void endCall() throws SipException {
- if (DBG) log("endCall: mSipSession" + mSipSession);
- synchronized (this) {
- stopCall(RELEASE_SOCKET);
- mInCall = false;
-
- // perform the above local ops first and then network op
- if (mSipSession != null) mSipSession.endCall();
- }
- }
-
- /**
- * Puts a call on hold. When succeeds, {@link Listener#onCallHeld} is
- * called. The attempt will be timed out if the call is not established
- * within {@code timeout} seconds and
- * {@link Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
- * will be called.
- *
- * @param timeout the timeout value in seconds. Default value (defined by
- * SIP protocol) is used if {@code timeout} is zero or negative.
- * @see Listener#onError
- * @throws SipException if the SIP service fails to hold the call
- */
- public void holdCall(int timeout) throws SipException {
- if (DBG) log("holdCall: mSipSession" + mSipSession + " timeout=" + timeout);
- synchronized (this) {
- if (mHold) return;
- if (mSipSession == null) {
- loge("holdCall:");
- throw new SipException("Not in a call to hold call");
- }
- mSipSession.changeCall(createHoldOffer().encode(), timeout);
- mHold = true;
- setAudioGroupMode();
- }
- }
-
- /**
- * Answers a call. The attempt will be timed out if the call is not
- * established within {@code timeout} seconds and
- * {@link Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
- * will be called.
- *
- * @param timeout the timeout value in seconds. Default value (defined by
- * SIP protocol) is used if {@code timeout} is zero or negative.
- * @see Listener#onError
- * @throws SipException if the SIP service fails to answer the call
- */
- public void answerCall(int timeout) throws SipException {
- if (DBG) log("answerCall: mSipSession" + mSipSession + " timeout=" + timeout);
- synchronized (this) {
- if (mSipSession == null) {
- throw new SipException("No call to answer");
- }
- try {
- mAudioStream = new AudioStream(InetAddress.getByName(
- getLocalIp()));
- mSipSession.answerCall(createAnswer(mPeerSd).encode(), timeout);
- } catch (IOException e) {
- loge("answerCall:", e);
- throw new SipException("answerCall()", e);
- }
- }
- }
-
- /**
- * Continues a call that's on hold. When succeeds,
- * {@link Listener#onCallEstablished} is called. The attempt will be timed
- * out if the call is not established within {@code timeout} seconds and
- * {@link Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
- * will be called.
- *
- * @param timeout the timeout value in seconds. Default value (defined by
- * SIP protocol) is used if {@code timeout} is zero or negative.
- * @see Listener#onError
- * @throws SipException if the SIP service fails to unhold the call
- */
- public void continueCall(int timeout) throws SipException {
- if (DBG) log("continueCall: mSipSession" + mSipSession + " timeout=" + timeout);
- synchronized (this) {
- if (!mHold) return;
- mSipSession.changeCall(createContinueOffer().encode(), timeout);
- mHold = false;
- setAudioGroupMode();
- }
- }
-
- private SimpleSessionDescription createOffer() {
- SimpleSessionDescription offer =
- new SimpleSessionDescription(mSessionId, getLocalIp());
- AudioCodec[] codecs = AudioCodec.getCodecs();
- Media media = offer.newMedia(
- "audio", mAudioStream.getLocalPort(), 1, "RTP/AVP");
- for (AudioCodec codec : AudioCodec.getCodecs()) {
- media.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp);
- }
- media.setRtpPayload(127, "telephone-event/8000", "0-15");
- if (DBG) log("createOffer: offer=" + offer);
- return offer;
- }
-
- private SimpleSessionDescription createAnswer(String offerSd) {
- if (TextUtils.isEmpty(offerSd)) return createOffer();
- SimpleSessionDescription offer =
- new SimpleSessionDescription(offerSd);
- SimpleSessionDescription answer =
- new SimpleSessionDescription(mSessionId, getLocalIp());
- AudioCodec codec = null;
- for (Media media : offer.getMedia()) {
- if ((codec == null) && (media.getPort() > 0)
- && "audio".equals(media.getType())
- && "RTP/AVP".equals(media.getProtocol())) {
- // Find the first audio codec we supported.
- for (int type : media.getRtpPayloadTypes()) {
- codec = AudioCodec.getCodec(type, media.getRtpmap(type),
- media.getFmtp(type));
- if (codec != null) {
- break;
- }
- }
- if (codec != null) {
- Media reply = answer.newMedia(
- "audio", mAudioStream.getLocalPort(), 1, "RTP/AVP");
- reply.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp);
-
- // Check if DTMF is supported in the same media.
- for (int type : media.getRtpPayloadTypes()) {
- String rtpmap = media.getRtpmap(type);
- if ((type != codec.type) && (rtpmap != null)
- && rtpmap.startsWith("telephone-event")) {
- reply.setRtpPayload(
- type, rtpmap, media.getFmtp(type));
- }
- }
-
- // Handle recvonly and sendonly.
- if (media.getAttribute("recvonly") != null) {
- answer.setAttribute("sendonly", "");
- } else if(media.getAttribute("sendonly") != null) {
- answer.setAttribute("recvonly", "");
- } else if(offer.getAttribute("recvonly") != null) {
- answer.setAttribute("sendonly", "");
- } else if(offer.getAttribute("sendonly") != null) {
- answer.setAttribute("recvonly", "");
- }
- continue;
- }
- }
- // Reject the media.
- Media reply = answer.newMedia(
- media.getType(), 0, 1, media.getProtocol());
- for (String format : media.getFormats()) {
- reply.setFormat(format, null);
- }
- }
- if (codec == null) {
- loge("createAnswer: no suitable codes");
- throw new IllegalStateException("Reject SDP: no suitable codecs");
- }
- if (DBG) log("createAnswer: answer=" + answer);
- return answer;
- }
-
- private SimpleSessionDescription createHoldOffer() {
- SimpleSessionDescription offer = createContinueOffer();
- offer.setAttribute("sendonly", "");
- if (DBG) log("createHoldOffer: offer=" + offer);
- return offer;
- }
-
- private SimpleSessionDescription createContinueOffer() {
- if (DBG) log("createContinueOffer");
- SimpleSessionDescription offer =
- new SimpleSessionDescription(mSessionId, getLocalIp());
- Media media = offer.newMedia(
- "audio", mAudioStream.getLocalPort(), 1, "RTP/AVP");
- AudioCodec codec = mAudioStream.getCodec();
- media.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp);
- int dtmfType = mAudioStream.getDtmfType();
- if (dtmfType != -1) {
- media.setRtpPayload(dtmfType, "telephone-event/8000", "0-15");
- }
- return offer;
- }
-
- private void grabWifiHighPerfLock() {
- if (mWifiHighPerfLock == null) {
- if (DBG) log("grabWifiHighPerfLock:");
- mWifiHighPerfLock = ((WifiManager)
- mContext.getSystemService(Context.WIFI_SERVICE))
- .createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, LOG_TAG);
- mWifiHighPerfLock.acquire();
- }
- }
-
- private void releaseWifiHighPerfLock() {
- if (mWifiHighPerfLock != null) {
- if (DBG) log("releaseWifiHighPerfLock:");
- mWifiHighPerfLock.release();
- mWifiHighPerfLock = null;
- }
- }
-
- private boolean isWifiOn() {
- return (mWm.getConnectionInfo().getBSSID() == null) ? false : true;
- }
-
- /** Toggles mute. */
- public void toggleMute() {
- synchronized (this) {
- mMuted = !mMuted;
- setAudioGroupMode();
- }
- }
-
- /**
- * Checks if the call is muted.
- *
- * @return true if the call is muted
- */
- public boolean isMuted() {
- synchronized (this) {
- return mMuted;
- }
- }
-
- /**
- * Puts the device to speaker mode.
- * <p class="note"><strong>Note:</strong> Requires the
- * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS} permission.</p>
- *
- * @param speakerMode set true to enable speaker mode; false to disable
- */
- public void setSpeakerMode(boolean speakerMode) {
- synchronized (this) {
- ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
- .setSpeakerphoneOn(speakerMode);
- setAudioGroupMode();
- }
- }
-
- private boolean isSpeakerOn() {
- return ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
- .isSpeakerphoneOn();
- }
-
- /**
- * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2883</a>,
- * event 0--9 maps to decimal
- * value 0--9, '*' to 10, '#' to 11, event 'A'--'D' to 12--15, and event
- * flash to 16. Currently, event flash is not supported.
- *
- * @param code the DTMF code to send. Value 0 to 15 (inclusive) are valid
- * inputs.
- */
- public void sendDtmf(int code) {
- sendDtmf(code, null);
- }
-
- /**
- * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2883</a>,
- * event 0--9 maps to decimal
- * value 0--9, '*' to 10, '#' to 11, event 'A'--'D' to 12--15, and event
- * flash to 16. Currently, event flash is not supported.
- *
- * @param code the DTMF code to send. Value 0 to 15 (inclusive) are valid
- * inputs.
- * @param result the result message to send when done
- */
- public void sendDtmf(int code, Message result) {
- synchronized (this) {
- AudioGroup audioGroup = getAudioGroup();
- if ((audioGroup != null) && (mSipSession != null)
- && (SipSession.State.IN_CALL == getState())) {
- if (DBG) log("sendDtmf: code=" + code + " result=" + result);
- audioGroup.sendDtmf(code);
- }
- if (result != null) result.sendToTarget();
- }
- }
-
- /**
- * Gets the {@link AudioStream} object used in this call. The object
- * represents the RTP stream that carries the audio data to and from the
- * peer. The object may not be created before the call is established. And
- * it is undefined after the call ends or the {@link #close} method is
- * called.
- *
- * @return the {@link AudioStream} object or null if the RTP stream has not
- * yet been set up
- * @hide
- */
- public AudioStream getAudioStream() {
- synchronized (this) {
- return mAudioStream;
- }
- }
-
- /**
- * Gets the {@link AudioGroup} object which the {@link AudioStream} object
- * joins. The group object may not exist before the call is established.
- * Also, the {@code AudioStream} may change its group during a call (e.g.,
- * after the call is held/un-held). Finally, the {@code AudioGroup} object
- * returned by this method is undefined after the call ends or the
- * {@link #close} method is called. If a group object is set by
- * {@link #setAudioGroup(AudioGroup)}, then this method returns that object.
- *
- * @return the {@link AudioGroup} object or null if the RTP stream has not
- * yet been set up
- * @see #getAudioStream
- * @hide
- */
- public AudioGroup getAudioGroup() {
- synchronized (this) {
- if (mAudioGroup != null) return mAudioGroup;
- return ((mAudioStream == null) ? null : mAudioStream.getGroup());
- }
- }
-
- /**
- * Sets the {@link AudioGroup} object which the {@link AudioStream} object
- * joins. If {@code audioGroup} is null, then the {@code AudioGroup} object
- * will be dynamically created when needed. Note that the mode of the
- * {@code AudioGroup} is not changed according to the audio settings (i.e.,
- * hold, mute, speaker phone) of this object. This is mainly used to merge
- * multiple {@code SipAudioCall} objects to form a conference call. The
- * settings of the first object (that merges others) override others'.
- *
- * @see #getAudioStream
- * @hide
- */
- public void setAudioGroup(AudioGroup group) {
- synchronized (this) {
- if (DBG) log("setAudioGroup: group=" + group);
- if ((mAudioStream != null) && (mAudioStream.getGroup() != null)) {
- mAudioStream.join(group);
- }
- mAudioGroup = group;
- }
- }
-
- /**
- * Starts the audio for the established call. This method should be called
- * after {@link Listener#onCallEstablished} is called.
- * <p class="note"><strong>Note:</strong> Requires the
- * {@link android.Manifest.permission#RECORD_AUDIO},
- * {@link android.Manifest.permission#ACCESS_WIFI_STATE} and
- * {@link android.Manifest.permission#WAKE_LOCK} permissions.</p>
- */
- public void startAudio() {
- try {
- startAudioInternal();
- } catch (UnknownHostException e) {
- onError(SipErrorCode.PEER_NOT_REACHABLE, e.getMessage());
- } catch (Throwable e) {
- onError(SipErrorCode.CLIENT_ERROR, e.getMessage());
- }
- }
-
- private synchronized void startAudioInternal() throws UnknownHostException {
- if (DBG) loge("startAudioInternal: mPeerSd=" + mPeerSd);
- if (mPeerSd == null) {
- throw new IllegalStateException("mPeerSd = null");
- }
-
- stopCall(DONT_RELEASE_SOCKET);
- mInCall = true;
-
- // Run exact the same logic in createAnswer() to setup mAudioStream.
- SimpleSessionDescription offer =
- new SimpleSessionDescription(mPeerSd);
- AudioStream stream = mAudioStream;
- AudioCodec codec = null;
- for (Media media : offer.getMedia()) {
- if ((codec == null) && (media.getPort() > 0)
- && "audio".equals(media.getType())
- && "RTP/AVP".equals(media.getProtocol())) {
- // Find the first audio codec we supported.
- for (int type : media.getRtpPayloadTypes()) {
- codec = AudioCodec.getCodec(
- type, media.getRtpmap(type), media.getFmtp(type));
- if (codec != null) {
- break;
- }
- }
-
- if (codec != null) {
- // Associate with the remote host.
- String address = media.getAddress();
- if (address == null) {
- address = offer.getAddress();
- }
- stream.associate(InetAddress.getByName(address),
- media.getPort());
-
- stream.setDtmfType(-1);
- stream.setCodec(codec);
- // Check if DTMF is supported in the same media.
- for (int type : media.getRtpPayloadTypes()) {
- String rtpmap = media.getRtpmap(type);
- if ((type != codec.type) && (rtpmap != null)
- && rtpmap.startsWith("telephone-event")) {
- stream.setDtmfType(type);
- }
- }
-
- // Handle recvonly and sendonly.
- if (mHold) {
- stream.setMode(RtpStream.MODE_NORMAL);
- } else if (media.getAttribute("recvonly") != null) {
- stream.setMode(RtpStream.MODE_SEND_ONLY);
- } else if(media.getAttribute("sendonly") != null) {
- stream.setMode(RtpStream.MODE_RECEIVE_ONLY);
- } else if(offer.getAttribute("recvonly") != null) {
- stream.setMode(RtpStream.MODE_SEND_ONLY);
- } else if(offer.getAttribute("sendonly") != null) {
- stream.setMode(RtpStream.MODE_RECEIVE_ONLY);
- } else {
- stream.setMode(RtpStream.MODE_NORMAL);
- }
- break;
- }
- }
- }
- if (codec == null) {
- throw new IllegalStateException("Reject SDP: no suitable codecs");
- }
-
- if (isWifiOn()) grabWifiHighPerfLock();
-
- // AudioGroup logic:
- AudioGroup audioGroup = getAudioGroup();
- if (mHold) {
- // don't create an AudioGroup here; doing so will fail if
- // there's another AudioGroup out there that's active
- } else {
- if (audioGroup == null) audioGroup = new AudioGroup();
- stream.join(audioGroup);
- }
- setAudioGroupMode();
- }
-
- // set audio group mode based on current audio configuration
- private void setAudioGroupMode() {
- AudioGroup audioGroup = getAudioGroup();
- if (DBG) log("setAudioGroupMode: audioGroup=" + audioGroup);
- if (audioGroup != null) {
- if (mHold) {
- audioGroup.setMode(AudioGroup.MODE_ON_HOLD);
- } else if (mMuted) {
- audioGroup.setMode(AudioGroup.MODE_MUTED);
- } else if (isSpeakerOn()) {
- audioGroup.setMode(AudioGroup.MODE_ECHO_SUPPRESSION);
- } else {
- audioGroup.setMode(AudioGroup.MODE_NORMAL);
- }
- }
- }
-
- private void stopCall(boolean releaseSocket) {
- if (DBG) log("stopCall: releaseSocket=" + releaseSocket);
- releaseWifiHighPerfLock();
- if (mAudioStream != null) {
- mAudioStream.join(null);
-
- if (releaseSocket) {
- mAudioStream.release();
- mAudioStream = null;
- }
- }
- }
-
- private String getLocalIp() {
- return mSipSession.getLocalIp();
- }
-
- private void throwSipException(Throwable throwable) throws SipException {
- if (throwable instanceof SipException) {
- throw (SipException) throwable;
- } else {
- throw new SipException("", throwable);
- }
- }
-
- private void log(String s) {
- Rlog.d(LOG_TAG, s);
- }
-
- private void loge(String s) {
- Rlog.e(LOG_TAG, s);
- }
-
- private void loge(String s, Throwable t) {
- Rlog.e(LOG_TAG, s, t);
- }
-}
diff --git a/voip/java/android/net/sip/SipErrorCode.java b/voip/java/android/net/sip/SipErrorCode.java
deleted file mode 100644
index 509728f..0000000
--- a/voip/java/android/net/sip/SipErrorCode.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-/**
- * Defines error codes returned during SIP actions. For example, during
- * {@link SipRegistrationListener#onRegistrationFailed onRegistrationFailed()},
- * {@link SipSession.Listener#onError onError()},
- * {@link SipSession.Listener#onCallChangeFailed onCallChangeFailed()} and
- * {@link SipSession.Listener#onRegistrationFailed onRegistrationFailed()}.
- */
-public class SipErrorCode {
- /** Not an error. */
- public static final int NO_ERROR = 0;
-
- /** When some socket error occurs. */
- public static final int SOCKET_ERROR = -1;
-
- /** When server responds with an error. */
- public static final int SERVER_ERROR = -2;
-
- /** When transaction is terminated unexpectedly. */
- public static final int TRANSACTION_TERMINTED = -3;
-
- /** When some error occurs on the device, possibly due to a bug. */
- public static final int CLIENT_ERROR = -4;
-
- /** When the transaction gets timed out. */
- public static final int TIME_OUT = -5;
-
- /** When the remote URI is not valid. */
- public static final int INVALID_REMOTE_URI = -6;
-
- /** When the peer is not reachable. */
- public static final int PEER_NOT_REACHABLE = -7;
-
- /** When invalid credentials are provided. */
- public static final int INVALID_CREDENTIALS = -8;
-
- /** The client is in a transaction and cannot initiate a new one. */
- public static final int IN_PROGRESS = -9;
-
- /** When data connection is lost. */
- public static final int DATA_CONNECTION_LOST = -10;
-
- /** Cross-domain authentication required. */
- public static final int CROSS_DOMAIN_AUTHENTICATION = -11;
-
- /** When the server is not reachable. */
- public static final int SERVER_UNREACHABLE = -12;
-
- public static String toString(int errorCode) {
- switch (errorCode) {
- case NO_ERROR:
- return "NO_ERROR";
- case SOCKET_ERROR:
- return "SOCKET_ERROR";
- case SERVER_ERROR:
- return "SERVER_ERROR";
- case TRANSACTION_TERMINTED:
- return "TRANSACTION_TERMINTED";
- case CLIENT_ERROR:
- return "CLIENT_ERROR";
- case TIME_OUT:
- return "TIME_OUT";
- case INVALID_REMOTE_URI:
- return "INVALID_REMOTE_URI";
- case PEER_NOT_REACHABLE:
- return "PEER_NOT_REACHABLE";
- case INVALID_CREDENTIALS:
- return "INVALID_CREDENTIALS";
- case IN_PROGRESS:
- return "IN_PROGRESS";
- case DATA_CONNECTION_LOST:
- return "DATA_CONNECTION_LOST";
- case CROSS_DOMAIN_AUTHENTICATION:
- return "CROSS_DOMAIN_AUTHENTICATION";
- case SERVER_UNREACHABLE:
- return "SERVER_UNREACHABLE";
- default:
- return "UNKNOWN";
- }
- }
-
- private SipErrorCode() {
- }
-}
diff --git a/voip/java/android/net/sip/SipException.java b/voip/java/android/net/sip/SipException.java
deleted file mode 100644
index 0339395..0000000
--- a/voip/java/android/net/sip/SipException.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-/**
- * Indicates a general SIP-related exception.
- */
-public class SipException extends Exception {
- public SipException() {
- }
-
- public SipException(String message) {
- super(message);
- }
-
- public SipException(String message, Throwable cause) {
- // we want to eliminate the dependency on javax.sip.SipException
- super(message, ((cause instanceof javax.sip.SipException)
- && (cause.getCause() != null))
- ? cause.getCause()
- : cause);
- }
-}
diff --git a/voip/java/android/net/sip/SipManager.java b/voip/java/android/net/sip/SipManager.java
deleted file mode 100644
index a94232a..0000000
--- a/voip/java/android/net/sip/SipManager.java
+++ /dev/null
@@ -1,622 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.telephony.Rlog;
-
-import java.text.ParseException;
-
-/**
- * Provides APIs for SIP tasks, such as initiating SIP connections, and provides access to related
- * SIP services. This class is the starting point for any SIP actions. You can acquire an instance
- * of it with {@link #newInstance newInstance()}.</p>
- * <p>The APIs in this class allows you to:</p>
- * <ul>
- * <li>Create a {@link SipSession} to get ready for making calls or listen for incoming calls. See
- * {@link #createSipSession createSipSession()} and {@link #getSessionFor getSessionFor()}.</li>
- * <li>Initiate and receive generic SIP calls or audio-only SIP calls. Generic SIP calls may
- * be video, audio, or other, and are initiated with {@link #open open()}. Audio-only SIP calls
- * should be handled with a {@link SipAudioCall}, which you can acquire with {@link
- * #makeAudioCall makeAudioCall()} and {@link #takeAudioCall takeAudioCall()}.</li>
- * <li>Register and unregister with a SIP service provider, with
- * {@link #register register()} and {@link #unregister unregister()}.</li>
- * <li>Verify session connectivity, with {@link #isOpened isOpened()} and
- * {@link #isRegistered isRegistered()}.</li>
- * </ul>
- * <p class="note"><strong>Note:</strong> Not all Android-powered devices support VOIP calls using
- * SIP. You should always call {@link android.net.sip.SipManager#isVoipSupported
- * isVoipSupported()} to verify that the device supports VOIP calling and {@link
- * android.net.sip.SipManager#isApiSupported isApiSupported()} to verify that the device supports
- * the SIP APIs. Your application must also request the {@link
- * android.Manifest.permission#INTERNET} and {@link android.Manifest.permission#USE_SIP}
- * permissions.</p>
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For more information about using SIP, read the
- * <a href="{@docRoot}guide/topics/network/sip.html">Session Initiation Protocol</a>
- * developer guide.</p>
- * </div>
- */
-public class SipManager {
- /**
- * The result code to be sent back with the incoming call
- * {@link PendingIntent}.
- * @see #open(SipProfile, PendingIntent, SipRegistrationListener)
- */
- public static final int INCOMING_CALL_RESULT_CODE = 101;
-
- /**
- * Key to retrieve the call ID from an incoming call intent.
- * @see #open(SipProfile, PendingIntent, SipRegistrationListener)
- */
- public static final String EXTRA_CALL_ID = "android:sipCallID";
-
- /**
- * Key to retrieve the offered session description from an incoming call
- * intent.
- * @see #open(SipProfile, PendingIntent, SipRegistrationListener)
- */
- public static final String EXTRA_OFFER_SD = "android:sipOfferSD";
-
- /**
- * Action to broadcast when SipService is up.
- * Internal use only.
- * @hide
- */
- public static final String ACTION_SIP_SERVICE_UP =
- "android.net.sip.SIP_SERVICE_UP";
- /**
- * Action string for the incoming call intent for the Phone app.
- * Internal use only.
- * @hide
- */
- public static final String ACTION_SIP_INCOMING_CALL =
- "com.android.phone.SIP_INCOMING_CALL";
- /**
- * Action string for the add-phone intent.
- * Internal use only.
- * @hide
- */
- public static final String ACTION_SIP_ADD_PHONE =
- "com.android.phone.SIP_ADD_PHONE";
- /**
- * Action string for the remove-phone intent.
- * Internal use only.
- * @hide
- */
- public static final String ACTION_SIP_REMOVE_PHONE =
- "com.android.phone.SIP_REMOVE_PHONE";
- /**
- * Part of the ACTION_SIP_ADD_PHONE and ACTION_SIP_REMOVE_PHONE intents.
- * Internal use only.
- * @hide
- */
- public static final String EXTRA_LOCAL_URI = "android:localSipUri";
-
- private static final String TAG = "SipManager";
-
- private ISipService mSipService;
- private Context mContext;
-
- /**
- * Creates a manager instance. Returns null if SIP API is not supported.
- *
- * @param context application context for creating the manager object
- * @return the manager instance or null if SIP API is not supported
- */
- public static SipManager newInstance(Context context) {
- return (isApiSupported(context) ? new SipManager(context) : null);
- }
-
- /**
- * Returns true if the SIP API is supported by the system.
- */
- public static boolean isApiSupported(Context context) {
- return context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_SIP);
- }
-
- /**
- * Returns true if the system supports SIP-based VOIP API.
- */
- public static boolean isVoipSupported(Context context) {
- return context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_SIP_VOIP) && isApiSupported(context);
- }
-
- /**
- * Returns true if SIP is only available on WIFI.
- */
- public static boolean isSipWifiOnly(Context context) {
- return context.getResources().getBoolean(
- com.android.internal.R.bool.config_sip_wifi_only);
- }
-
- private SipManager(Context context) {
- mContext = context;
- createSipService();
- }
-
- private void createSipService() {
- IBinder b = ServiceManager.getService(Context.SIP_SERVICE);
- mSipService = ISipService.Stub.asInterface(b);
- }
-
- /**
- * Opens the profile for making generic SIP calls. The caller may make subsequent calls
- * through {@link #makeAudioCall}. If one also wants to receive calls on the
- * profile, use
- * {@link #open(SipProfile, PendingIntent, SipRegistrationListener)}
- * instead.
- *
- * @param localProfile the SIP profile to make calls from
- * @throws SipException if the profile contains incorrect settings or
- * calling the SIP service results in an error
- */
- public void open(SipProfile localProfile) throws SipException {
- try {
- mSipService.open(localProfile);
- } catch (RemoteException e) {
- throw new SipException("open()", e);
- }
- }
-
- /**
- * Opens the profile for making calls and/or receiving generic SIP calls. The caller may
- * make subsequent calls through {@link #makeAudioCall}. If the
- * auto-registration option is enabled in the profile, the SIP service
- * will register the profile to the corresponding SIP provider periodically
- * in order to receive calls from the provider. When the SIP service
- * receives a new call, it will send out an intent with the provided action
- * string. The intent contains a call ID extra and an offer session
- * description string extra. Use {@link #getCallId} and
- * {@link #getOfferSessionDescription} to retrieve those extras.
- *
- * @param localProfile the SIP profile to receive incoming calls for
- * @param incomingCallPendingIntent When an incoming call is received, the
- * SIP service will call
- * {@link PendingIntent#send(Context, int, Intent)} to send back the
- * intent to the caller with {@link #INCOMING_CALL_RESULT_CODE} as the
- * result code and the intent to fill in the call ID and session
- * description information. It cannot be null.
- * @param listener to listen to registration events; can be null
- * @see #getCallId
- * @see #getOfferSessionDescription
- * @see #takeAudioCall
- * @throws NullPointerException if {@code incomingCallPendingIntent} is null
- * @throws SipException if the profile contains incorrect settings or
- * calling the SIP service results in an error
- * @see #isIncomingCallIntent
- * @see #getCallId
- * @see #getOfferSessionDescription
- */
- public void open(SipProfile localProfile,
- PendingIntent incomingCallPendingIntent,
- SipRegistrationListener listener) throws SipException {
- if (incomingCallPendingIntent == null) {
- throw new NullPointerException(
- "incomingCallPendingIntent cannot be null");
- }
- try {
- mSipService.open3(localProfile, incomingCallPendingIntent,
- createRelay(listener, localProfile.getUriString()));
- } catch (RemoteException e) {
- throw new SipException("open()", e);
- }
- }
-
- /**
- * Sets the listener to listen to registration events. No effect if the
- * profile has not been opened to receive calls (see
- * {@link #open(SipProfile, PendingIntent, SipRegistrationListener)}).
- *
- * @param localProfileUri the URI of the profile
- * @param listener to listen to registration events; can be null
- * @throws SipException if calling the SIP service results in an error
- */
- public void setRegistrationListener(String localProfileUri,
- SipRegistrationListener listener) throws SipException {
- try {
- mSipService.setRegistrationListener(
- localProfileUri, createRelay(listener, localProfileUri));
- } catch (RemoteException e) {
- throw new SipException("setRegistrationListener()", e);
- }
- }
-
- /**
- * Closes the specified profile to not make/receive calls. All the resources
- * that were allocated to the profile are also released.
- *
- * @param localProfileUri the URI of the profile to close
- * @throws SipException if calling the SIP service results in an error
- */
- public void close(String localProfileUri) throws SipException {
- try {
- mSipService.close(localProfileUri);
- } catch (RemoteException e) {
- throw new SipException("close()", e);
- }
- }
-
- /**
- * Checks if the specified profile is opened in the SIP service for
- * making and/or receiving calls.
- *
- * @param localProfileUri the URI of the profile in question
- * @return true if the profile is enabled to receive calls
- * @throws SipException if calling the SIP service results in an error
- */
- public boolean isOpened(String localProfileUri) throws SipException {
- try {
- return mSipService.isOpened(localProfileUri);
- } catch (RemoteException e) {
- throw new SipException("isOpened()", e);
- }
- }
-
- /**
- * Checks if the SIP service has successfully registered the profile to the
- * SIP provider (specified in the profile) for receiving calls. Returning
- * true from this method also implies the profile is opened
- * ({@link #isOpened}).
- *
- * @param localProfileUri the URI of the profile in question
- * @return true if the profile is registered to the SIP provider; false if
- * the profile has not been opened in the SIP service or the SIP
- * service has not yet successfully registered the profile to the SIP
- * provider
- * @throws SipException if calling the SIP service results in an error
- */
- public boolean isRegistered(String localProfileUri) throws SipException {
- try {
- return mSipService.isRegistered(localProfileUri);
- } catch (RemoteException e) {
- throw new SipException("isRegistered()", e);
- }
- }
-
- /**
- * Creates a {@link SipAudioCall} to make a call. The attempt will be timed
- * out if the call is not established within {@code timeout} seconds and
- * {@link SipAudioCall.Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
- * will be called.
- *
- * @param localProfile the SIP profile to make the call from
- * @param peerProfile the SIP profile to make the call to
- * @param listener to listen to the call events from {@link SipAudioCall};
- * can be null
- * @param timeout the timeout value in seconds. Default value (defined by
- * SIP protocol) is used if {@code timeout} is zero or negative.
- * @return a {@link SipAudioCall} object
- * @throws SipException if calling the SIP service results in an error or
- * VOIP API is not supported by the device
- * @see SipAudioCall.Listener#onError
- * @see #isVoipSupported
- */
- public SipAudioCall makeAudioCall(SipProfile localProfile,
- SipProfile peerProfile, SipAudioCall.Listener listener, int timeout)
- throws SipException {
- if (!isVoipSupported(mContext)) {
- throw new SipException("VOIP API is not supported");
- }
- SipAudioCall call = new SipAudioCall(mContext, localProfile);
- call.setListener(listener);
- SipSession s = createSipSession(localProfile, null);
- call.makeCall(peerProfile, s, timeout);
- return call;
- }
-
- /**
- * Creates a {@link SipAudioCall} to make an audio call. The attempt will be
- * timed out if the call is not established within {@code timeout} seconds
- * and
- * {@link SipAudioCall.Listener#onError onError(SipAudioCall, SipErrorCode.TIME_OUT, String)}
- * will be called.
- *
- * @param localProfileUri URI of the SIP profile to make the call from
- * @param peerProfileUri URI of the SIP profile to make the call to
- * @param listener to listen to the call events from {@link SipAudioCall};
- * can be null
- * @param timeout the timeout value in seconds. Default value (defined by
- * SIP protocol) is used if {@code timeout} is zero or negative.
- * @return a {@link SipAudioCall} object
- * @throws SipException if calling the SIP service results in an error or
- * VOIP API is not supported by the device
- * @see SipAudioCall.Listener#onError
- * @see #isVoipSupported
- */
- public SipAudioCall makeAudioCall(String localProfileUri,
- String peerProfileUri, SipAudioCall.Listener listener, int timeout)
- throws SipException {
- if (!isVoipSupported(mContext)) {
- throw new SipException("VOIP API is not supported");
- }
- try {
- return makeAudioCall(
- new SipProfile.Builder(localProfileUri).build(),
- new SipProfile.Builder(peerProfileUri).build(), listener,
- timeout);
- } catch (ParseException e) {
- throw new SipException("build SipProfile", e);
- }
- }
-
- /**
- * Creates a {@link SipAudioCall} to take an incoming call. Before the call
- * is returned, the listener will receive a
- * {@link SipAudioCall.Listener#onRinging}
- * callback.
- *
- * @param incomingCallIntent the incoming call broadcast intent
- * @param listener to listen to the call events from {@link SipAudioCall};
- * can be null
- * @return a {@link SipAudioCall} object
- * @throws SipException if calling the SIP service results in an error
- */
- public SipAudioCall takeAudioCall(Intent incomingCallIntent,
- SipAudioCall.Listener listener) throws SipException {
- if (incomingCallIntent == null) {
- throw new SipException("Cannot retrieve session with null intent");
- }
-
- String callId = getCallId(incomingCallIntent);
- if (callId == null) {
- throw new SipException("Call ID missing in incoming call intent");
- }
-
- String offerSd = getOfferSessionDescription(incomingCallIntent);
- if (offerSd == null) {
- throw new SipException("Session description missing in incoming "
- + "call intent");
- }
-
- try {
- ISipSession session = mSipService.getPendingSession(callId);
- if (session == null) {
- throw new SipException("No pending session for the call");
- }
- SipAudioCall call = new SipAudioCall(
- mContext, session.getLocalProfile());
- call.attachCall(new SipSession(session), offerSd);
- call.setListener(listener);
- return call;
- } catch (Throwable t) {
- throw new SipException("takeAudioCall()", t);
- }
- }
-
- /**
- * Checks if the intent is an incoming call broadcast intent.
- *
- * @param intent the intent in question
- * @return true if the intent is an incoming call broadcast intent
- */
- public static boolean isIncomingCallIntent(Intent intent) {
- if (intent == null) return false;
- String callId = getCallId(intent);
- String offerSd = getOfferSessionDescription(intent);
- return ((callId != null) && (offerSd != null));
- }
-
- /**
- * Gets the call ID from the specified incoming call broadcast intent.
- *
- * @param incomingCallIntent the incoming call broadcast intent
- * @return the call ID or null if the intent does not contain it
- */
- public static String getCallId(Intent incomingCallIntent) {
- return incomingCallIntent.getStringExtra(EXTRA_CALL_ID);
- }
-
- /**
- * Gets the offer session description from the specified incoming call
- * broadcast intent.
- *
- * @param incomingCallIntent the incoming call broadcast intent
- * @return the offer session description or null if the intent does not
- * have it
- */
- public static String getOfferSessionDescription(Intent incomingCallIntent) {
- return incomingCallIntent.getStringExtra(EXTRA_OFFER_SD);
- }
-
- /**
- * Creates an incoming call broadcast intent.
- *
- * @param callId the call ID of the incoming call
- * @param sessionDescription the session description of the incoming call
- * @return the incoming call intent
- * @hide
- */
- public static Intent createIncomingCallBroadcast(String callId,
- String sessionDescription) {
- Intent intent = new Intent();
- intent.putExtra(EXTRA_CALL_ID, callId);
- intent.putExtra(EXTRA_OFFER_SD, sessionDescription);
- return intent;
- }
-
- /**
- * Manually registers the profile to the corresponding SIP provider for
- * receiving calls.
- * {@link #open(SipProfile, PendingIntent, SipRegistrationListener)} is
- * still needed to be called at least once in order for the SIP service to
- * notify the caller with the {@link android.app.PendingIntent} when an incoming call is
- * received.
- *
- * @param localProfile the SIP profile to register with
- * @param expiryTime registration expiration time (in seconds)
- * @param listener to listen to the registration events
- * @throws SipException if calling the SIP service results in an error
- */
- public void register(SipProfile localProfile, int expiryTime,
- SipRegistrationListener listener) throws SipException {
- try {
- ISipSession session = mSipService.createSession(localProfile,
- createRelay(listener, localProfile.getUriString()));
- if (session == null) {
- throw new SipException(
- "SipService.createSession() returns null");
- }
- session.register(expiryTime);
- } catch (RemoteException e) {
- throw new SipException("register()", e);
- }
- }
-
- /**
- * Manually unregisters the profile from the corresponding SIP provider for
- * stop receiving further calls. This may interference with the auto
- * registration process in the SIP service if the auto-registration option
- * in the profile is enabled.
- *
- * @param localProfile the SIP profile to register with
- * @param listener to listen to the registration events
- * @throws SipException if calling the SIP service results in an error
- */
- public void unregister(SipProfile localProfile,
- SipRegistrationListener listener) throws SipException {
- try {
- ISipSession session = mSipService.createSession(localProfile,
- createRelay(listener, localProfile.getUriString()));
- if (session == null) {
- throw new SipException(
- "SipService.createSession() returns null");
- }
- session.unregister();
- } catch (RemoteException e) {
- throw new SipException("unregister()", e);
- }
- }
-
- /**
- * Gets the {@link SipSession} that handles the incoming call. For audio
- * calls, consider to use {@link SipAudioCall} to handle the incoming call.
- * See {@link #takeAudioCall}. Note that the method may be called only once
- * for the same intent. For subsequent calls on the same intent, the method
- * returns null.
- *
- * @param incomingCallIntent the incoming call broadcast intent
- * @return the session object that handles the incoming call
- */
- public SipSession getSessionFor(Intent incomingCallIntent)
- throws SipException {
- try {
- String callId = getCallId(incomingCallIntent);
- ISipSession s = mSipService.getPendingSession(callId);
- return ((s == null) ? null : new SipSession(s));
- } catch (RemoteException e) {
- throw new SipException("getSessionFor()", e);
- }
- }
-
- private static ISipSessionListener createRelay(
- SipRegistrationListener listener, String uri) {
- return ((listener == null) ? null : new ListenerRelay(listener, uri));
- }
-
- /**
- * Creates a {@link SipSession} with the specified profile. Use other
- * methods, if applicable, instead of interacting with {@link SipSession}
- * directly.
- *
- * @param localProfile the SIP profile the session is associated with
- * @param listener to listen to SIP session events
- */
- public SipSession createSipSession(SipProfile localProfile,
- SipSession.Listener listener) throws SipException {
- try {
- ISipSession s = mSipService.createSession(localProfile, null);
- if (s == null) {
- throw new SipException(
- "Failed to create SipSession; network unavailable?");
- }
- return new SipSession(s, listener);
- } catch (RemoteException e) {
- throw new SipException("createSipSession()", e);
- }
- }
-
- /**
- * Gets the list of profiles hosted by the SIP service. The user information
- * (username, password and display name) are crossed out.
- * @hide
- */
- public SipProfile[] getListOfProfiles() {
- try {
- return mSipService.getListOfProfiles();
- } catch (RemoteException e) {
- return new SipProfile[0];
- }
- }
-
- private static class ListenerRelay extends SipSessionAdapter {
- private SipRegistrationListener mListener;
- private String mUri;
-
- // listener must not be null
- public ListenerRelay(SipRegistrationListener listener, String uri) {
- mListener = listener;
- mUri = uri;
- }
-
- private String getUri(ISipSession session) {
- try {
- return ((session == null)
- ? mUri
- : session.getLocalProfile().getUriString());
- } catch (Throwable e) {
- // SipService died? SIP stack died?
- Rlog.e(TAG, "getUri(): ", e);
- return null;
- }
- }
-
- @Override
- public void onRegistering(ISipSession session) {
- mListener.onRegistering(getUri(session));
- }
-
- @Override
- public void onRegistrationDone(ISipSession session, int duration) {
- long expiryTime = duration;
- if (duration > 0) expiryTime += System.currentTimeMillis();
- mListener.onRegistrationDone(getUri(session), expiryTime);
- }
-
- @Override
- public void onRegistrationFailed(ISipSession session, int errorCode,
- String message) {
- mListener.onRegistrationFailed(getUri(session), errorCode, message);
- }
-
- @Override
- public void onRegistrationTimeout(ISipSession session) {
- mListener.onRegistrationFailed(getUri(session),
- SipErrorCode.TIME_OUT, "registration timed out");
- }
- }
-}
diff --git a/voip/java/android/net/sip/SipProfile.aidl b/voip/java/android/net/sip/SipProfile.aidl
deleted file mode 100644
index 3b6f68f..0000000
--- a/voip/java/android/net/sip/SipProfile.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2010, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-parcelable SipProfile;
diff --git a/voip/java/android/net/sip/SipProfile.java b/voip/java/android/net/sip/SipProfile.java
deleted file mode 100644
index 0ef754c..0000000
--- a/voip/java/android/net/sip/SipProfile.java
+++ /dev/null
@@ -1,502 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-
-import java.io.ObjectStreamException;
-import java.io.Serializable;
-import java.text.ParseException;
-import javax.sip.InvalidArgumentException;
-import javax.sip.ListeningPoint;
-import javax.sip.PeerUnavailableException;
-import javax.sip.SipFactory;
-import javax.sip.address.Address;
-import javax.sip.address.AddressFactory;
-import javax.sip.address.SipURI;
-import javax.sip.address.URI;
-
-/**
- * Defines a SIP profile, including a SIP account, domain and server information.
- * <p>You can create a {@link SipProfile} using {@link
- * SipProfile.Builder}. You can also retrieve one from a {@link SipSession}, using {@link
- * SipSession#getLocalProfile} and {@link SipSession#getPeerProfile}.</p>
- *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For more information about using SIP, read the
- * <a href="{@docRoot}guide/topics/network/sip.html">Session Initiation Protocol</a>
- * developer guide.</p>
- * </div>
- */
-public class SipProfile implements Parcelable, Serializable, Cloneable {
- private static final long serialVersionUID = 1L;
- private static final int DEFAULT_PORT = 5060;
- private static final String TCP = "TCP";
- private static final String UDP = "UDP";
- private Address mAddress;
- private String mProxyAddress;
- private String mPassword;
- private String mDomain;
- private String mProtocol = UDP;
- private String mProfileName;
- private String mAuthUserName;
- private int mPort = DEFAULT_PORT;
- private boolean mSendKeepAlive = false;
- private boolean mAutoRegistration = true;
- private transient int mCallingUid = 0;
-
- public static final Parcelable.Creator<SipProfile> CREATOR =
- new Parcelable.Creator<SipProfile>() {
- public SipProfile createFromParcel(Parcel in) {
- return new SipProfile(in);
- }
-
- public SipProfile[] newArray(int size) {
- return new SipProfile[size];
- }
- };
-
- /**
- * Helper class for creating a {@link SipProfile}.
- */
- public static class Builder {
- private AddressFactory mAddressFactory;
- private SipProfile mProfile = new SipProfile();
- private SipURI mUri;
- private String mDisplayName;
- private String mProxyAddress;
-
- {
- try {
- mAddressFactory =
- SipFactory.getInstance().createAddressFactory();
- } catch (PeerUnavailableException e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Creates a builder based on the given profile.
- */
- public Builder(SipProfile profile) {
- if (profile == null) throw new NullPointerException();
- try {
- mProfile = (SipProfile) profile.clone();
- } catch (CloneNotSupportedException e) {
- throw new RuntimeException("should not occur", e);
- }
- mProfile.mAddress = null;
- mUri = profile.getUri();
- mUri.setUserPassword(profile.getPassword());
- mDisplayName = profile.getDisplayName();
- mProxyAddress = profile.getProxyAddress();
- mProfile.mPort = profile.getPort();
- }
-
- /**
- * Constructor.
- *
- * @param uriString the URI string as "sip:<user_name>@<domain>"
- * @throws ParseException if the string is not a valid URI
- */
- public Builder(String uriString) throws ParseException {
- if (uriString == null) {
- throw new NullPointerException("uriString cannot be null");
- }
- URI uri = mAddressFactory.createURI(fix(uriString));
- if (uri instanceof SipURI) {
- mUri = (SipURI) uri;
- } else {
- throw new ParseException(uriString + " is not a SIP URI", 0);
- }
- mProfile.mDomain = mUri.getHost();
- }
-
- /**
- * Constructor.
- *
- * @param username username of the SIP account
- * @param serverDomain the SIP server domain; if the network address
- * is different from the domain, use {@link #setOutboundProxy} to
- * set server address
- * @throws ParseException if the parameters are not valid
- */
- public Builder(String username, String serverDomain)
- throws ParseException {
- if ((username == null) || (serverDomain == null)) {
- throw new NullPointerException(
- "username and serverDomain cannot be null");
- }
- mUri = mAddressFactory.createSipURI(username, serverDomain);
- mProfile.mDomain = serverDomain;
- }
-
- private String fix(String uriString) {
- return (uriString.trim().toLowerCase().startsWith("sip:")
- ? uriString
- : "sip:" + uriString);
- }
-
- /**
- * Sets the username used for authentication.
- *
- * @param name authentication username of the profile
- * @return this builder object
- */
- public Builder setAuthUserName(String name) {
- mProfile.mAuthUserName = name;
- return this;
- }
-
- /**
- * Sets the name of the profile. This name is given by user.
- *
- * @param name name of the profile
- * @return this builder object
- */
- public Builder setProfileName(String name) {
- mProfile.mProfileName = name;
- return this;
- }
-
- /**
- * Sets the password of the SIP account
- *
- * @param password password of the SIP account
- * @return this builder object
- */
- public Builder setPassword(String password) {
- mUri.setUserPassword(password);
- return this;
- }
-
- /**
- * Sets the port number of the server. By default, it is 5060.
- *
- * @param port port number of the server
- * @return this builder object
- * @throws IllegalArgumentException if the port number is out of range
- */
- public Builder setPort(int port) throws IllegalArgumentException {
- if ((port > 65535) || (port < 1000)) {
- throw new IllegalArgumentException("incorrect port arugment: " + port);
- }
- mProfile.mPort = port;
- return this;
- }
-
- /**
- * Sets the protocol used to connect to the SIP server. Currently,
- * only "UDP" and "TCP" are supported.
- *
- * @param protocol the protocol string
- * @return this builder object
- * @throws IllegalArgumentException if the protocol is not recognized
- */
- public Builder setProtocol(String protocol)
- throws IllegalArgumentException {
- if (protocol == null) {
- throw new NullPointerException("protocol cannot be null");
- }
- protocol = protocol.toUpperCase();
- if (!protocol.equals(UDP) && !protocol.equals(TCP)) {
- throw new IllegalArgumentException(
- "unsupported protocol: " + protocol);
- }
- mProfile.mProtocol = protocol;
- return this;
- }
-
- /**
- * Sets the outbound proxy of the SIP server.
- *
- * @param outboundProxy the network address of the outbound proxy
- * @return this builder object
- */
- public Builder setOutboundProxy(String outboundProxy) {
- mProxyAddress = outboundProxy;
- return this;
- }
-
- /**
- * Sets the display name of the user.
- *
- * @param displayName display name of the user
- * @return this builder object
- */
- public Builder setDisplayName(String displayName) {
- mDisplayName = displayName;
- return this;
- }
-
- /**
- * Sets the send keep-alive flag.
- *
- * @param flag true if sending keep-alive message is required,
- * false otherwise
- * @return this builder object
- */
- public Builder setSendKeepAlive(boolean flag) {
- mProfile.mSendKeepAlive = flag;
- return this;
- }
-
-
- /**
- * Sets the auto. registration flag.
- *
- * @param flag true if the profile will be registered automatically,
- * false otherwise
- * @return this builder object
- */
- public Builder setAutoRegistration(boolean flag) {
- mProfile.mAutoRegistration = flag;
- return this;
- }
-
- /**
- * Builds and returns the SIP profile object.
- *
- * @return the profile object created
- */
- public SipProfile build() {
- // remove password from URI
- mProfile.mPassword = mUri.getUserPassword();
- mUri.setUserPassword(null);
- try {
- if (!TextUtils.isEmpty(mProxyAddress)) {
- SipURI uri = (SipURI)
- mAddressFactory.createURI(fix(mProxyAddress));
- mProfile.mProxyAddress = uri.getHost();
- } else {
- if (!mProfile.mProtocol.equals(UDP)) {
- mUri.setTransportParam(mProfile.mProtocol);
- }
- if (mProfile.mPort != DEFAULT_PORT) {
- mUri.setPort(mProfile.mPort);
- }
- }
- mProfile.mAddress = mAddressFactory.createAddress(
- mDisplayName, mUri);
- } catch (InvalidArgumentException e) {
- throw new RuntimeException(e);
- } catch (ParseException e) {
- // must not occur
- throw new RuntimeException(e);
- }
- return mProfile;
- }
- }
-
- private SipProfile() {
- }
-
- private SipProfile(Parcel in) {
- mAddress = (Address) in.readSerializable();
- mProxyAddress = in.readString();
- mPassword = in.readString();
- mDomain = in.readString();
- mProtocol = in.readString();
- mProfileName = in.readString();
- mSendKeepAlive = (in.readInt() == 0) ? false : true;
- mAutoRegistration = (in.readInt() == 0) ? false : true;
- mCallingUid = in.readInt();
- mPort = in.readInt();
- mAuthUserName = in.readString();
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeSerializable(mAddress);
- out.writeString(mProxyAddress);
- out.writeString(mPassword);
- out.writeString(mDomain);
- out.writeString(mProtocol);
- out.writeString(mProfileName);
- out.writeInt(mSendKeepAlive ? 1 : 0);
- out.writeInt(mAutoRegistration ? 1 : 0);
- out.writeInt(mCallingUid);
- out.writeInt(mPort);
- out.writeString(mAuthUserName);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- /**
- * Gets the SIP URI of this profile.
- *
- * @return the SIP URI of this profile
- * @hide
- */
- public SipURI getUri() {
- return (SipURI) mAddress.getURI();
- }
-
- /**
- * Gets the SIP URI string of this profile.
- *
- * @return the SIP URI string of this profile
- */
- public String getUriString() {
- // We need to return the sip uri domain instead of
- // the SIP URI with transport, port information if
- // the outbound proxy address exists.
- if (!TextUtils.isEmpty(mProxyAddress)) {
- return "sip:" + getUserName() + "@" + mDomain;
- }
- return getUri().toString();
- }
-
- /**
- * Gets the SIP address of this profile.
- *
- * @return the SIP address of this profile
- * @hide
- */
- public Address getSipAddress() {
- return mAddress;
- }
-
- /**
- * Gets the display name of the user.
- *
- * @return the display name of the user
- */
- public String getDisplayName() {
- return mAddress.getDisplayName();
- }
-
- /**
- * Gets the username.
- *
- * @return the username
- */
- public String getUserName() {
- return getUri().getUser();
- }
-
- /**
- * Gets the username for authentication. If it is null, then the username
- * is used in authentication instead.
- *
- * @return the authentication username
- * @see #getUserName
- */
- public String getAuthUserName() {
- return mAuthUserName;
- }
-
- /**
- * Gets the password.
- *
- * @return the password
- */
- public String getPassword() {
- return mPassword;
- }
-
- /**
- * Gets the SIP domain.
- *
- * @return the SIP domain
- */
- public String getSipDomain() {
- return mDomain;
- }
-
- /**
- * Gets the port number of the SIP server.
- *
- * @return the port number of the SIP server
- */
- public int getPort() {
- return mPort;
- }
-
- /**
- * Gets the protocol used to connect to the server.
- *
- * @return the protocol
- */
- public String getProtocol() {
- return mProtocol;
- }
-
- /**
- * Gets the network address of the server outbound proxy.
- *
- * @return the network address of the server outbound proxy
- */
- public String getProxyAddress() {
- return mProxyAddress;
- }
-
- /**
- * Gets the (user-defined) name of the profile.
- *
- * @return name of the profile
- */
- public String getProfileName() {
- return mProfileName;
- }
-
- /**
- * Gets the flag of 'Sending keep-alive'.
- *
- * @return the flag of sending SIP keep-alive messages.
- */
- public boolean getSendKeepAlive() {
- return mSendKeepAlive;
- }
-
- /**
- * Gets the flag of 'Auto Registration'.
- *
- * @return the flag of registering the profile automatically.
- */
- public boolean getAutoRegistration() {
- return mAutoRegistration;
- }
-
- /**
- * Sets the calling process's Uid in the sip service.
- * @hide
- */
- public void setCallingUid(int uid) {
- mCallingUid = uid;
- }
-
- /**
- * Gets the calling process's Uid in the sip settings.
- * @hide
- */
- public int getCallingUid() {
- return mCallingUid;
- }
-
- private Object readResolve() throws ObjectStreamException {
- // For compatibility.
- if (mPort == 0) mPort = DEFAULT_PORT;
- return this;
- }
-}
diff --git a/voip/java/android/net/sip/SipRegistrationListener.java b/voip/java/android/net/sip/SipRegistrationListener.java
deleted file mode 100644
index 9968cc7..0000000
--- a/voip/java/android/net/sip/SipRegistrationListener.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-/**
- * Listener for SIP registration events.
- */
-public interface SipRegistrationListener {
- /**
- * Called when a registration request is sent.
- *
- * @param localProfileUri the URI string of the SIP profile to register with
- */
- void onRegistering(String localProfileUri);
-
- /**
- * Called when the registration succeeded.
- *
- * @param localProfileUri the URI string of the SIP profile to register with
- * @param expiryTime duration in seconds before the registration expires
- */
- void onRegistrationDone(String localProfileUri, long expiryTime);
-
- /**
- * Called when the registration failed.
- *
- * @param localProfileUri the URI string of the SIP profile to register with
- * @param errorCode error code of this error
- * @param errorMessage error message
- * @see SipErrorCode
- */
- void onRegistrationFailed(String localProfileUri, int errorCode,
- String errorMessage);
-}
diff --git a/voip/java/android/net/sip/SipSession.java b/voip/java/android/net/sip/SipSession.java
deleted file mode 100644
index edbc66f..0000000
--- a/voip/java/android/net/sip/SipSession.java
+++ /dev/null
@@ -1,574 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-import android.os.RemoteException;
-import android.telephony.Rlog;
-
-/**
- * Represents a SIP session that is associated with a SIP dialog or a standalone
- * transaction not within a dialog.
- * <p>You can get a {@link SipSession} from {@link SipManager} with {@link
- * SipManager#createSipSession createSipSession()} (when initiating calls) or {@link
- * SipManager#getSessionFor getSessionFor()} (when receiving calls).</p>
- */
-public final class SipSession {
- private static final String TAG = "SipSession";
-
- /**
- * Defines SIP session states, such as "registering", "outgoing call", and "in call".
- */
- public static class State {
- /** When session is ready to initiate a call or transaction. */
- public static final int READY_TO_CALL = 0;
-
- /** When the registration request is sent out. */
- public static final int REGISTERING = 1;
-
- /** When the unregistration request is sent out. */
- public static final int DEREGISTERING = 2;
-
- /** When an INVITE request is received. */
- public static final int INCOMING_CALL = 3;
-
- /** When an OK response is sent for the INVITE request received. */
- public static final int INCOMING_CALL_ANSWERING = 4;
-
- /** When an INVITE request is sent. */
- public static final int OUTGOING_CALL = 5;
-
- /** When a RINGING response is received for the INVITE request sent. */
- public static final int OUTGOING_CALL_RING_BACK = 6;
-
- /** When a CANCEL request is sent for the INVITE request sent. */
- public static final int OUTGOING_CALL_CANCELING = 7;
-
- /** When a call is established. */
- public static final int IN_CALL = 8;
-
- /** When an OPTIONS request is sent. */
- public static final int PINGING = 9;
-
- /** When ending a call. @hide */
- public static final int ENDING_CALL = 10;
-
- /** Not defined. */
- public static final int NOT_DEFINED = 101;
-
- /**
- * Converts the state to string.
- */
- public static String toString(int state) {
- switch (state) {
- case READY_TO_CALL:
- return "READY_TO_CALL";
- case REGISTERING:
- return "REGISTERING";
- case DEREGISTERING:
- return "DEREGISTERING";
- case INCOMING_CALL:
- return "INCOMING_CALL";
- case INCOMING_CALL_ANSWERING:
- return "INCOMING_CALL_ANSWERING";
- case OUTGOING_CALL:
- return "OUTGOING_CALL";
- case OUTGOING_CALL_RING_BACK:
- return "OUTGOING_CALL_RING_BACK";
- case OUTGOING_CALL_CANCELING:
- return "OUTGOING_CALL_CANCELING";
- case IN_CALL:
- return "IN_CALL";
- case PINGING:
- return "PINGING";
- default:
- return "NOT_DEFINED";
- }
- }
-
- private State() {
- }
- }
-
- /**
- * Listener for events relating to a SIP session, such as when a session is being registered
- * ("on registering") or a call is outgoing ("on calling").
- * <p>Many of these events are also received by {@link SipAudioCall.Listener}.</p>
- */
- public static class Listener {
- /**
- * Called when an INVITE request is sent to initiate a new call.
- *
- * @param session the session object that carries out the transaction
- */
- public void onCalling(SipSession session) {
- }
-
- /**
- * Called when an INVITE request is received.
- *
- * @param session the session object that carries out the transaction
- * @param caller the SIP profile of the caller
- * @param sessionDescription the caller's session description
- */
- public void onRinging(SipSession session, SipProfile caller,
- String sessionDescription) {
- }
-
- /**
- * Called when a RINGING response is received for the INVITE request sent
- *
- * @param session the session object that carries out the transaction
- */
- public void onRingingBack(SipSession session) {
- }
-
- /**
- * Called when the session is established.
- *
- * @param session the session object that is associated with the dialog
- * @param sessionDescription the peer's session description
- */
- public void onCallEstablished(SipSession session,
- String sessionDescription) {
- }
-
- /**
- * Called when the session is terminated.
- *
- * @param session the session object that is associated with the dialog
- */
- public void onCallEnded(SipSession session) {
- }
-
- /**
- * Called when the peer is busy during session initialization.
- *
- * @param session the session object that carries out the transaction
- */
- public void onCallBusy(SipSession session) {
- }
-
- /**
- * Called when the call is being transferred to a new one.
- *
- * @hide
- * @param newSession the new session that the call will be transferred to
- * @param sessionDescription the new peer's session description
- */
- public void onCallTransferring(SipSession newSession,
- String sessionDescription) {
- }
-
- /**
- * Called when an error occurs during session initialization and
- * termination.
- *
- * @param session the session object that carries out the transaction
- * @param errorCode error code defined in {@link SipErrorCode}
- * @param errorMessage error message
- */
- public void onError(SipSession session, int errorCode,
- String errorMessage) {
- }
-
- /**
- * Called when an error occurs during session modification negotiation.
- *
- * @param session the session object that carries out the transaction
- * @param errorCode error code defined in {@link SipErrorCode}
- * @param errorMessage error message
- */
- public void onCallChangeFailed(SipSession session, int errorCode,
- String errorMessage) {
- }
-
- /**
- * Called when a registration request is sent.
- *
- * @param session the session object that carries out the transaction
- */
- public void onRegistering(SipSession session) {
- }
-
- /**
- * Called when registration is successfully done.
- *
- * @param session the session object that carries out the transaction
- * @param duration duration in second before the registration expires
- */
- public void onRegistrationDone(SipSession session, int duration) {
- }
-
- /**
- * Called when the registration fails.
- *
- * @param session the session object that carries out the transaction
- * @param errorCode error code defined in {@link SipErrorCode}
- * @param errorMessage error message
- */
- public void onRegistrationFailed(SipSession session, int errorCode,
- String errorMessage) {
- }
-
- /**
- * Called when the registration gets timed out.
- *
- * @param session the session object that carries out the transaction
- */
- public void onRegistrationTimeout(SipSession session) {
- }
- }
-
- private final ISipSession mSession;
- private Listener mListener;
-
- SipSession(ISipSession realSession) {
- mSession = realSession;
- if (realSession != null) {
- try {
- realSession.setListener(createListener());
- } catch (RemoteException e) {
- loge("SipSession.setListener:", e);
- }
- }
- }
-
- SipSession(ISipSession realSession, Listener listener) {
- this(realSession);
- setListener(listener);
- }
-
- /**
- * Gets the IP address of the local host on which this SIP session runs.
- *
- * @return the IP address of the local host
- */
- public String getLocalIp() {
- try {
- return mSession.getLocalIp();
- } catch (RemoteException e) {
- loge("getLocalIp:", e);
- return "127.0.0.1";
- }
- }
-
- /**
- * Gets the SIP profile that this session is associated with.
- *
- * @return the SIP profile that this session is associated with
- */
- public SipProfile getLocalProfile() {
- try {
- return mSession.getLocalProfile();
- } catch (RemoteException e) {
- loge("getLocalProfile:", e);
- return null;
- }
- }
-
- /**
- * Gets the SIP profile that this session is connected to. Only available
- * when the session is associated with a SIP dialog.
- *
- * @return the SIP profile that this session is connected to
- */
- public SipProfile getPeerProfile() {
- try {
- return mSession.getPeerProfile();
- } catch (RemoteException e) {
- loge("getPeerProfile:", e);
- return null;
- }
- }
-
- /**
- * Gets the session state. The value returned must be one of the states in
- * {@link State}.
- *
- * @return the session state
- */
- public int getState() {
- try {
- return mSession.getState();
- } catch (RemoteException e) {
- loge("getState:", e);
- return State.NOT_DEFINED;
- }
- }
-
- /**
- * Checks if the session is in a call.
- *
- * @return true if the session is in a call
- */
- public boolean isInCall() {
- try {
- return mSession.isInCall();
- } catch (RemoteException e) {
- loge("isInCall:", e);
- return false;
- }
- }
-
- /**
- * Gets the call ID of the session.
- *
- * @return the call ID
- */
- public String getCallId() {
- try {
- return mSession.getCallId();
- } catch (RemoteException e) {
- loge("getCallId:", e);
- return null;
- }
- }
-
-
- /**
- * Sets the listener to listen to the session events. A {@code SipSession}
- * can only hold one listener at a time. Subsequent calls to this method
- * override the previous listener.
- *
- * @param listener to listen to the session events of this object
- */
- public void setListener(Listener listener) {
- mListener = listener;
- }
-
-
- /**
- * Performs registration to the server specified by the associated local
- * profile. The session listener is called back upon success or failure of
- * registration. The method is only valid to call when the session state is
- * in {@link State#READY_TO_CALL}.
- *
- * @param duration duration in second before the registration expires
- * @see Listener
- */
- public void register(int duration) {
- try {
- mSession.register(duration);
- } catch (RemoteException e) {
- loge("register:", e);
- }
- }
-
- /**
- * Performs unregistration to the server specified by the associated local
- * profile. Unregistration is technically the same as registration with zero
- * expiration duration. The session listener is called back upon success or
- * failure of unregistration. The method is only valid to call when the
- * session state is in {@link State#READY_TO_CALL}.
- *
- * @see Listener
- */
- public void unregister() {
- try {
- mSession.unregister();
- } catch (RemoteException e) {
- loge("unregister:", e);
- }
- }
-
- /**
- * Initiates a call to the specified profile. The session listener is called
- * back upon defined session events. The method is only valid to call when
- * the session state is in {@link State#READY_TO_CALL}.
- *
- * @param callee the SIP profile to make the call to
- * @param sessionDescription the session description of this call
- * @param timeout the session will be timed out if the call is not
- * established within {@code timeout} seconds. Default value (defined
- * by SIP protocol) is used if {@code timeout} is zero or negative.
- * @see Listener
- */
- public void makeCall(SipProfile callee, String sessionDescription,
- int timeout) {
- try {
- mSession.makeCall(callee, sessionDescription, timeout);
- } catch (RemoteException e) {
- loge("makeCall:", e);
- }
- }
-
- /**
- * Answers an incoming call with the specified session description. The
- * method is only valid to call when the session state is in
- * {@link State#INCOMING_CALL}.
- *
- * @param sessionDescription the session description to answer this call
- * @param timeout the session will be timed out if the call is not
- * established within {@code timeout} seconds. Default value (defined
- * by SIP protocol) is used if {@code timeout} is zero or negative.
- */
- public void answerCall(String sessionDescription, int timeout) {
- try {
- mSession.answerCall(sessionDescription, timeout);
- } catch (RemoteException e) {
- loge("answerCall:", e);
- }
- }
-
- /**
- * Ends an established call, terminates an outgoing call or rejects an
- * incoming call. The method is only valid to call when the session state is
- * in {@link State#IN_CALL},
- * {@link State#INCOMING_CALL},
- * {@link State#OUTGOING_CALL} or
- * {@link State#OUTGOING_CALL_RING_BACK}.
- */
- public void endCall() {
- try {
- mSession.endCall();
- } catch (RemoteException e) {
- loge("endCall:", e);
- }
- }
-
- /**
- * Changes the session description during a call. The method is only valid
- * to call when the session state is in {@link State#IN_CALL}.
- *
- * @param sessionDescription the new session description
- * @param timeout the session will be timed out if the call is not
- * established within {@code timeout} seconds. Default value (defined
- * by SIP protocol) is used if {@code timeout} is zero or negative.
- */
- public void changeCall(String sessionDescription, int timeout) {
- try {
- mSession.changeCall(sessionDescription, timeout);
- } catch (RemoteException e) {
- loge("changeCall:", e);
- }
- }
-
- ISipSession getRealSession() {
- return mSession;
- }
-
- private ISipSessionListener createListener() {
- return new ISipSessionListener.Stub() {
- @Override
- public void onCalling(ISipSession session) {
- if (mListener != null) {
- mListener.onCalling(SipSession.this);
- }
- }
-
- @Override
- public void onRinging(ISipSession session, SipProfile caller,
- String sessionDescription) {
- if (mListener != null) {
- mListener.onRinging(SipSession.this, caller,
- sessionDescription);
- }
- }
-
- @Override
- public void onRingingBack(ISipSession session) {
- if (mListener != null) {
- mListener.onRingingBack(SipSession.this);
- }
- }
-
- @Override
- public void onCallEstablished(ISipSession session,
- String sessionDescription) {
- if (mListener != null) {
- mListener.onCallEstablished(SipSession.this,
- sessionDescription);
- }
- }
-
- @Override
- public void onCallEnded(ISipSession session) {
- if (mListener != null) {
- mListener.onCallEnded(SipSession.this);
- }
- }
-
- @Override
- public void onCallBusy(ISipSession session) {
- if (mListener != null) {
- mListener.onCallBusy(SipSession.this);
- }
- }
-
- @Override
- public void onCallTransferring(ISipSession session,
- String sessionDescription) {
- if (mListener != null) {
- mListener.onCallTransferring(
- new SipSession(session, SipSession.this.mListener),
- sessionDescription);
-
- }
- }
-
- @Override
- public void onCallChangeFailed(ISipSession session, int errorCode,
- String message) {
- if (mListener != null) {
- mListener.onCallChangeFailed(SipSession.this, errorCode,
- message);
- }
- }
-
- @Override
- public void onError(ISipSession session, int errorCode, String message) {
- if (mListener != null) {
- mListener.onError(SipSession.this, errorCode, message);
- }
- }
-
- @Override
- public void onRegistering(ISipSession session) {
- if (mListener != null) {
- mListener.onRegistering(SipSession.this);
- }
- }
-
- @Override
- public void onRegistrationDone(ISipSession session, int duration) {
- if (mListener != null) {
- mListener.onRegistrationDone(SipSession.this, duration);
- }
- }
-
- @Override
- public void onRegistrationFailed(ISipSession session, int errorCode,
- String message) {
- if (mListener != null) {
- mListener.onRegistrationFailed(SipSession.this, errorCode,
- message);
- }
- }
-
- @Override
- public void onRegistrationTimeout(ISipSession session) {
- if (mListener != null) {
- mListener.onRegistrationTimeout(SipSession.this);
- }
- }
- };
- }
-
- private void loge(String s, Throwable t) {
- Rlog.e(TAG, s, t);
- }
-}
diff --git a/voip/java/android/net/sip/SipSessionAdapter.java b/voip/java/android/net/sip/SipSessionAdapter.java
deleted file mode 100644
index f538983..0000000
--- a/voip/java/android/net/sip/SipSessionAdapter.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-/**
- * Adapter class for {@link ISipSessionListener}. Default implementation of all
- * callback methods is no-op.
- * @hide
- */
-public class SipSessionAdapter extends ISipSessionListener.Stub {
- public void onCalling(ISipSession session) {
- }
-
- public void onRinging(ISipSession session, SipProfile caller,
- String sessionDescription) {
- }
-
- public void onRingingBack(ISipSession session) {
- }
-
- public void onCallEstablished(ISipSession session,
- String sessionDescription) {
- }
-
- public void onCallEnded(ISipSession session) {
- }
-
- public void onCallBusy(ISipSession session) {
- }
-
- public void onCallTransferring(ISipSession session,
- String sessionDescription) {
- }
-
- public void onCallChangeFailed(ISipSession session, int errorCode,
- String message) {
- }
-
- public void onError(ISipSession session, int errorCode, String message) {
- }
-
- public void onRegistering(ISipSession session) {
- }
-
- public void onRegistrationDone(ISipSession session, int duration) {
- }
-
- public void onRegistrationFailed(ISipSession session, int errorCode,
- String message) {
- }
-
- public void onRegistrationTimeout(ISipSession session) {
- }
-}
diff --git a/voip/java/android/net/sip/package.html b/voip/java/android/net/sip/package.html
deleted file mode 100644
index 3c4cc23..0000000
--- a/voip/java/android/net/sip/package.html
+++ /dev/null
@@ -1,45 +0,0 @@
-<HTML>
-<BODY>
-<p>Provides access to Session Initiation Protocol (SIP) functionality, such as
-making and answering VOIP calls using SIP.</p>
-
-<p>For more information, see the
-<a href="{@docRoot}guide/topics/connectivity/sip.html">Session Initiation Protocol</a>
-developer guide.</p>
-{@more}
-
-<p>To get started, you need to get an instance of the {@link android.net.sip.SipManager} by
-calling {@link android.net.sip.SipManager#newInstance newInstance()}.</p>
-
-<p>With the {@link android.net.sip.SipManager}, you can initiate SIP audio calls with {@link
-android.net.sip.SipManager#makeAudioCall makeAudioCall()} and {@link
-android.net.sip.SipManager#takeAudioCall takeAudioCall()}. Both methods require
-a {@link android.net.sip.SipAudioCall.Listener} that receives callbacks when the state of the
-call changes, such as when the call is ringing, established, or ended.</p>
-
-<p>Both {@link android.net.sip.SipManager#makeAudioCall makeAudioCall()} also requires two
-{@link android.net.sip.SipProfile} objects, representing the local device and the peer
-device. You can create a {@link android.net.sip.SipProfile} using the {@link
-android.net.sip.SipProfile.Builder} subclass.</p>
-
-<p>Once you have a {@link android.net.sip.SipAudioCall}, you can perform SIP audio call actions with
-the instance, such as make a call, answer a call, mute a call, turn on speaker mode, send DTMF
-tones, and more.</p>
-
-<p>If you want to create generic SIP connections (such as for video calls or other), you can
-create a SIP connection from the {@link android.net.sip.SipManager}, using {@link
-android.net.sip.SipManager#open open()}. If you only want to create audio SIP calls, though, you
-should use the {@link android.net.sip.SipAudioCall} class, as described above.</p>
-
-<p class="note"><strong>Note:</strong>
-Not all Android-powered devices support VOIP functionality with SIP. Before performing any SIP
-activity, you should call {@link android.net.sip.SipManager#isVoipSupported isVoipSupported()}
-to verify that the device supports VOIP calling and {@link
-android.net.sip.SipManager#isApiSupported isApiSupported()} to verify that the device supports the
-SIP APIs.
-Your application must also request the {@link android.Manifest.permission#INTERNET} and {@link
-android.Manifest.permission#USE_SIP} permissions in order to use the SIP APIs.
-</p>
-
-</BODY>
-</HTML>
\ No newline at end of file
diff --git a/voip/java/com/android/server/sip/SipHelper.java b/voip/java/com/android/server/sip/SipHelper.java
deleted file mode 100644
index c708be8..0000000
--- a/voip/java/com/android/server/sip/SipHelper.java
+++ /dev/null
@@ -1,537 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.sip;
-
-import gov.nist.javax.sip.SipStackExt;
-import gov.nist.javax.sip.clientauthutils.AccountManager;
-import gov.nist.javax.sip.clientauthutils.AuthenticationHelper;
-import gov.nist.javax.sip.header.extensions.ReferencesHeader;
-import gov.nist.javax.sip.header.extensions.ReferredByHeader;
-import gov.nist.javax.sip.header.extensions.ReplacesHeader;
-
-import android.net.sip.SipProfile;
-import android.telephony.Rlog;
-
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.EventObject;
-import java.util.List;
-import java.util.regex.Pattern;
-
-import javax.sip.ClientTransaction;
-import javax.sip.Dialog;
-import javax.sip.DialogTerminatedEvent;
-import javax.sip.InvalidArgumentException;
-import javax.sip.ListeningPoint;
-import javax.sip.PeerUnavailableException;
-import javax.sip.RequestEvent;
-import javax.sip.ResponseEvent;
-import javax.sip.ServerTransaction;
-import javax.sip.SipException;
-import javax.sip.SipFactory;
-import javax.sip.SipProvider;
-import javax.sip.SipStack;
-import javax.sip.Transaction;
-import javax.sip.TransactionTerminatedEvent;
-import javax.sip.TransactionState;
-import javax.sip.address.Address;
-import javax.sip.address.AddressFactory;
-import javax.sip.address.SipURI;
-import javax.sip.header.CSeqHeader;
-import javax.sip.header.CallIdHeader;
-import javax.sip.header.ContactHeader;
-import javax.sip.header.FromHeader;
-import javax.sip.header.Header;
-import javax.sip.header.HeaderFactory;
-import javax.sip.header.MaxForwardsHeader;
-import javax.sip.header.ToHeader;
-import javax.sip.header.ViaHeader;
-import javax.sip.message.Message;
-import javax.sip.message.MessageFactory;
-import javax.sip.message.Request;
-import javax.sip.message.Response;
-
-/**
- * Helper class for holding SIP stack related classes and for various low-level
- * SIP tasks like sending messages.
- */
-class SipHelper {
- private static final String TAG = SipHelper.class.getSimpleName();
- private static final boolean DBG = false;
- private static final boolean DBG_PING = false;
-
- private SipStack mSipStack;
- private SipProvider mSipProvider;
- private AddressFactory mAddressFactory;
- private HeaderFactory mHeaderFactory;
- private MessageFactory mMessageFactory;
-
- public SipHelper(SipStack sipStack, SipProvider sipProvider)
- throws PeerUnavailableException {
- mSipStack = sipStack;
- mSipProvider = sipProvider;
-
- SipFactory sipFactory = SipFactory.getInstance();
- mAddressFactory = sipFactory.createAddressFactory();
- mHeaderFactory = sipFactory.createHeaderFactory();
- mMessageFactory = sipFactory.createMessageFactory();
- }
-
- private FromHeader createFromHeader(SipProfile profile, String tag)
- throws ParseException {
- return mHeaderFactory.createFromHeader(profile.getSipAddress(), tag);
- }
-
- private ToHeader createToHeader(SipProfile profile) throws ParseException {
- return createToHeader(profile, null);
- }
-
- private ToHeader createToHeader(SipProfile profile, String tag)
- throws ParseException {
- return mHeaderFactory.createToHeader(profile.getSipAddress(), tag);
- }
-
- private CallIdHeader createCallIdHeader() {
- return mSipProvider.getNewCallId();
- }
-
- private CSeqHeader createCSeqHeader(String method)
- throws ParseException, InvalidArgumentException {
- long sequence = (long) (Math.random() * 10000);
- return mHeaderFactory.createCSeqHeader(sequence, method);
- }
-
- private MaxForwardsHeader createMaxForwardsHeader()
- throws InvalidArgumentException {
- return mHeaderFactory.createMaxForwardsHeader(70);
- }
-
- private MaxForwardsHeader createMaxForwardsHeader(int max)
- throws InvalidArgumentException {
- return mHeaderFactory.createMaxForwardsHeader(max);
- }
-
- private ListeningPoint getListeningPoint() throws SipException {
- ListeningPoint lp = mSipProvider.getListeningPoint(ListeningPoint.UDP);
- if (lp == null) lp = mSipProvider.getListeningPoint(ListeningPoint.TCP);
- if (lp == null) {
- ListeningPoint[] lps = mSipProvider.getListeningPoints();
- if ((lps != null) && (lps.length > 0)) lp = lps[0];
- }
- if (lp == null) {
- throw new SipException("no listening point is available");
- }
- return lp;
- }
-
- private List<ViaHeader> createViaHeaders()
- throws ParseException, SipException {
- List<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(1);
- ListeningPoint lp = getListeningPoint();
- ViaHeader viaHeader = mHeaderFactory.createViaHeader(lp.getIPAddress(),
- lp.getPort(), lp.getTransport(), null);
- viaHeader.setRPort();
- viaHeaders.add(viaHeader);
- return viaHeaders;
- }
-
- private ContactHeader createContactHeader(SipProfile profile)
- throws ParseException, SipException {
- return createContactHeader(profile, null, 0);
- }
-
- private ContactHeader createContactHeader(SipProfile profile,
- String ip, int port) throws ParseException,
- SipException {
- SipURI contactURI = (ip == null)
- ? createSipUri(profile.getUserName(), profile.getProtocol(),
- getListeningPoint())
- : createSipUri(profile.getUserName(), profile.getProtocol(),
- ip, port);
-
- Address contactAddress = mAddressFactory.createAddress(contactURI);
- contactAddress.setDisplayName(profile.getDisplayName());
-
- return mHeaderFactory.createContactHeader(contactAddress);
- }
-
- private ContactHeader createWildcardContactHeader() {
- ContactHeader contactHeader = mHeaderFactory.createContactHeader();
- contactHeader.setWildCard();
- return contactHeader;
- }
-
- private SipURI createSipUri(String username, String transport,
- ListeningPoint lp) throws ParseException {
- return createSipUri(username, transport, lp.getIPAddress(), lp.getPort());
- }
-
- private SipURI createSipUri(String username, String transport,
- String ip, int port) throws ParseException {
- SipURI uri = mAddressFactory.createSipURI(username, ip);
- try {
- uri.setPort(port);
- uri.setTransportParam(transport);
- } catch (InvalidArgumentException e) {
- throw new RuntimeException(e);
- }
- return uri;
- }
-
- public ClientTransaction sendOptions(SipProfile caller, SipProfile callee,
- String tag) throws SipException {
- try {
- Request request = (caller == callee)
- ? createRequest(Request.OPTIONS, caller, tag)
- : createRequest(Request.OPTIONS, caller, callee, tag);
-
- ClientTransaction clientTransaction =
- mSipProvider.getNewClientTransaction(request);
- clientTransaction.sendRequest();
- return clientTransaction;
- } catch (Exception e) {
- throw new SipException("sendOptions()", e);
- }
- }
-
- public ClientTransaction sendRegister(SipProfile userProfile, String tag,
- int expiry) throws SipException {
- try {
- Request request = createRequest(Request.REGISTER, userProfile, tag);
- if (expiry == 0) {
- // remove all previous registrations by wildcard
- // rfc3261#section-10.2.2
- request.addHeader(createWildcardContactHeader());
- } else {
- request.addHeader(createContactHeader(userProfile));
- }
- request.addHeader(mHeaderFactory.createExpiresHeader(expiry));
-
- ClientTransaction clientTransaction =
- mSipProvider.getNewClientTransaction(request);
- clientTransaction.sendRequest();
- return clientTransaction;
- } catch (ParseException e) {
- throw new SipException("sendRegister()", e);
- }
- }
-
- private Request createRequest(String requestType, SipProfile userProfile,
- String tag) throws ParseException, SipException {
- FromHeader fromHeader = createFromHeader(userProfile, tag);
- ToHeader toHeader = createToHeader(userProfile);
-
- String replaceStr = Pattern.quote(userProfile.getUserName() + "@");
- SipURI requestURI = mAddressFactory.createSipURI(
- userProfile.getUriString().replaceFirst(replaceStr, ""));
-
- List<ViaHeader> viaHeaders = createViaHeaders();
- CallIdHeader callIdHeader = createCallIdHeader();
- CSeqHeader cSeqHeader = createCSeqHeader(requestType);
- MaxForwardsHeader maxForwards = createMaxForwardsHeader();
- Request request = mMessageFactory.createRequest(requestURI,
- requestType, callIdHeader, cSeqHeader, fromHeader,
- toHeader, viaHeaders, maxForwards);
- Header userAgentHeader = mHeaderFactory.createHeader("User-Agent",
- "SIPAUA/0.1.001");
- request.addHeader(userAgentHeader);
- return request;
- }
-
- public ClientTransaction handleChallenge(ResponseEvent responseEvent,
- AccountManager accountManager) throws SipException {
- AuthenticationHelper authenticationHelper =
- ((SipStackExt) mSipStack).getAuthenticationHelper(
- accountManager, mHeaderFactory);
- ClientTransaction tid = responseEvent.getClientTransaction();
- ClientTransaction ct = authenticationHelper.handleChallenge(
- responseEvent.getResponse(), tid, mSipProvider, 5);
- if (DBG) log("send request with challenge response: "
- + ct.getRequest());
- ct.sendRequest();
- return ct;
- }
-
- private Request createRequest(String requestType, SipProfile caller,
- SipProfile callee, String tag) throws ParseException, SipException {
- FromHeader fromHeader = createFromHeader(caller, tag);
- ToHeader toHeader = createToHeader(callee);
- SipURI requestURI = callee.getUri();
- List<ViaHeader> viaHeaders = createViaHeaders();
- CallIdHeader callIdHeader = createCallIdHeader();
- CSeqHeader cSeqHeader = createCSeqHeader(requestType);
- MaxForwardsHeader maxForwards = createMaxForwardsHeader();
-
- Request request = mMessageFactory.createRequest(requestURI,
- requestType, callIdHeader, cSeqHeader, fromHeader,
- toHeader, viaHeaders, maxForwards);
-
- request.addHeader(createContactHeader(caller));
- return request;
- }
-
- public ClientTransaction sendInvite(SipProfile caller, SipProfile callee,
- String sessionDescription, String tag, ReferredByHeader referredBy,
- String replaces) throws SipException {
- try {
- Request request = createRequest(Request.INVITE, caller, callee, tag);
- if (referredBy != null) request.addHeader(referredBy);
- if (replaces != null) {
- request.addHeader(mHeaderFactory.createHeader(
- ReplacesHeader.NAME, replaces));
- }
- request.setContent(sessionDescription,
- mHeaderFactory.createContentTypeHeader(
- "application", "sdp"));
- ClientTransaction clientTransaction =
- mSipProvider.getNewClientTransaction(request);
- if (DBG) log("send INVITE: " + request);
- clientTransaction.sendRequest();
- return clientTransaction;
- } catch (ParseException e) {
- throw new SipException("sendInvite()", e);
- }
- }
-
- public ClientTransaction sendReinvite(Dialog dialog,
- String sessionDescription) throws SipException {
- try {
- Request request = dialog.createRequest(Request.INVITE);
- request.setContent(sessionDescription,
- mHeaderFactory.createContentTypeHeader(
- "application", "sdp"));
-
- // Adding rport argument in the request could fix some SIP servers
- // in resolving the initiator's NAT port mapping for relaying the
- // response message from the other end.
-
- ViaHeader viaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
- if (viaHeader != null) viaHeader.setRPort();
-
- ClientTransaction clientTransaction =
- mSipProvider.getNewClientTransaction(request);
- if (DBG) log("send RE-INVITE: " + request);
- dialog.sendRequest(clientTransaction);
- return clientTransaction;
- } catch (ParseException e) {
- throw new SipException("sendReinvite()", e);
- }
- }
-
- public ServerTransaction getServerTransaction(RequestEvent event)
- throws SipException {
- ServerTransaction transaction = event.getServerTransaction();
- if (transaction == null) {
- Request request = event.getRequest();
- return mSipProvider.getNewServerTransaction(request);
- } else {
- return transaction;
- }
- }
-
- /**
- * @param event the INVITE request event
- */
- public ServerTransaction sendRinging(RequestEvent event, String tag)
- throws SipException {
- try {
- Request request = event.getRequest();
- ServerTransaction transaction = getServerTransaction(event);
-
- Response response = mMessageFactory.createResponse(Response.RINGING,
- request);
-
- ToHeader toHeader = (ToHeader) response.getHeader(ToHeader.NAME);
- toHeader.setTag(tag);
- response.addHeader(toHeader);
- if (DBG) log("send RINGING: " + response);
- transaction.sendResponse(response);
- return transaction;
- } catch (ParseException e) {
- throw new SipException("sendRinging()", e);
- }
- }
-
- /**
- * @param event the INVITE request event
- */
- public ServerTransaction sendInviteOk(RequestEvent event,
- SipProfile localProfile, String sessionDescription,
- ServerTransaction inviteTransaction, String externalIp,
- int externalPort) throws SipException {
- try {
- Request request = event.getRequest();
- Response response = mMessageFactory.createResponse(Response.OK,
- request);
- response.addHeader(createContactHeader(localProfile, externalIp,
- externalPort));
- response.setContent(sessionDescription,
- mHeaderFactory.createContentTypeHeader(
- "application", "sdp"));
-
- if (inviteTransaction == null) {
- inviteTransaction = getServerTransaction(event);
- }
-
- if (inviteTransaction.getState() != TransactionState.COMPLETED) {
- if (DBG) log("send OK: " + response);
- inviteTransaction.sendResponse(response);
- }
-
- return inviteTransaction;
- } catch (ParseException e) {
- throw new SipException("sendInviteOk()", e);
- }
- }
-
- public void sendInviteBusyHere(RequestEvent event,
- ServerTransaction inviteTransaction) throws SipException {
- try {
- Request request = event.getRequest();
- Response response = mMessageFactory.createResponse(
- Response.BUSY_HERE, request);
-
- if (inviteTransaction == null) {
- inviteTransaction = getServerTransaction(event);
- }
-
- if (inviteTransaction.getState() != TransactionState.COMPLETED) {
- if (DBG) log("send BUSY HERE: " + response);
- inviteTransaction.sendResponse(response);
- }
- } catch (ParseException e) {
- throw new SipException("sendInviteBusyHere()", e);
- }
- }
-
- /**
- * @param event the INVITE ACK request event
- */
- public void sendInviteAck(ResponseEvent event, Dialog dialog)
- throws SipException {
- Response response = event.getResponse();
- long cseq = ((CSeqHeader) response.getHeader(CSeqHeader.NAME))
- .getSeqNumber();
- Request ack = dialog.createAck(cseq);
- if (DBG) log("send ACK: " + ack);
- dialog.sendAck(ack);
- }
-
- public void sendBye(Dialog dialog) throws SipException {
- Request byeRequest = dialog.createRequest(Request.BYE);
- if (DBG) log("send BYE: " + byeRequest);
- dialog.sendRequest(mSipProvider.getNewClientTransaction(byeRequest));
- }
-
- public void sendCancel(ClientTransaction inviteTransaction)
- throws SipException {
- Request cancelRequest = inviteTransaction.createCancel();
- if (DBG) log("send CANCEL: " + cancelRequest);
- mSipProvider.getNewClientTransaction(cancelRequest).sendRequest();
- }
-
- public void sendResponse(RequestEvent event, int responseCode)
- throws SipException {
- try {
- Request request = event.getRequest();
- Response response = mMessageFactory.createResponse(
- responseCode, request);
- if (DBG && (!Request.OPTIONS.equals(request.getMethod())
- || DBG_PING)) {
- log("send response: " + response);
- }
- getServerTransaction(event).sendResponse(response);
- } catch (ParseException e) {
- throw new SipException("sendResponse()", e);
- }
- }
-
- public void sendReferNotify(Dialog dialog, String content)
- throws SipException {
- try {
- Request request = dialog.createRequest(Request.NOTIFY);
- request.addHeader(mHeaderFactory.createSubscriptionStateHeader(
- "active;expires=60"));
- // set content here
- request.setContent(content,
- mHeaderFactory.createContentTypeHeader(
- "message", "sipfrag"));
- request.addHeader(mHeaderFactory.createEventHeader(
- ReferencesHeader.REFER));
- if (DBG) log("send NOTIFY: " + request);
- dialog.sendRequest(mSipProvider.getNewClientTransaction(request));
- } catch (ParseException e) {
- throw new SipException("sendReferNotify()", e);
- }
- }
-
- public void sendInviteRequestTerminated(Request inviteRequest,
- ServerTransaction inviteTransaction) throws SipException {
- try {
- Response response = mMessageFactory.createResponse(
- Response.REQUEST_TERMINATED, inviteRequest);
- if (DBG) log("send response: " + response);
- inviteTransaction.sendResponse(response);
- } catch (ParseException e) {
- throw new SipException("sendInviteRequestTerminated()", e);
- }
- }
-
- public static String getCallId(EventObject event) {
- if (event == null) return null;
- if (event instanceof RequestEvent) {
- return getCallId(((RequestEvent) event).getRequest());
- } else if (event instanceof ResponseEvent) {
- return getCallId(((ResponseEvent) event).getResponse());
- } else if (event instanceof DialogTerminatedEvent) {
- Dialog dialog = ((DialogTerminatedEvent) event).getDialog();
- return getCallId(((DialogTerminatedEvent) event).getDialog());
- } else if (event instanceof TransactionTerminatedEvent) {
- TransactionTerminatedEvent e = (TransactionTerminatedEvent) event;
- return getCallId(e.isServerTransaction()
- ? e.getServerTransaction()
- : e.getClientTransaction());
- } else {
- Object source = event.getSource();
- if (source instanceof Transaction) {
- return getCallId(((Transaction) source));
- } else if (source instanceof Dialog) {
- return getCallId((Dialog) source);
- }
- }
- return "";
- }
-
- public static String getCallId(Transaction transaction) {
- return ((transaction != null) ? getCallId(transaction.getRequest())
- : "");
- }
-
- private static String getCallId(Message message) {
- CallIdHeader callIdHeader =
- (CallIdHeader) message.getHeader(CallIdHeader.NAME);
- return callIdHeader.getCallId();
- }
-
- private static String getCallId(Dialog dialog) {
- return dialog.getCallId().getCallId();
- }
-
- private void log(String s) {
- Rlog.d(TAG, s);
- }
-}
diff --git a/voip/java/com/android/server/sip/SipService.java b/voip/java/com/android/server/sip/SipService.java
deleted file mode 100644
index 80fe68c..0000000
--- a/voip/java/com/android/server/sip/SipService.java
+++ /dev/null
@@ -1,1262 +0,0 @@
-/*
- * Copyright (C) 2010, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.sip;
-
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.net.sip.ISipService;
-import android.net.sip.ISipSession;
-import android.net.sip.ISipSessionListener;
-import android.net.sip.SipErrorCode;
-import android.net.sip.SipManager;
-import android.net.sip.SipProfile;
-import android.net.sip.SipSession;
-import android.net.sip.SipSessionAdapter;
-import android.net.wifi.WifiManager;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.telephony.Rlog;
-
-import java.io.IOException;
-import java.net.DatagramSocket;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.Executor;
-import javax.sip.SipException;
-
-/**
- * @hide
- */
-public final class SipService extends ISipService.Stub {
- static final String TAG = "SipService";
- static final boolean DBG = true;
- private static final int EXPIRY_TIME = 3600;
- private static final int SHORT_EXPIRY_TIME = 10;
- private static final int MIN_EXPIRY_TIME = 60;
- private static final int DEFAULT_KEEPALIVE_INTERVAL = 10; // in seconds
- private static final int DEFAULT_MAX_KEEPALIVE_INTERVAL = 120; // in seconds
-
- private Context mContext;
- private String mLocalIp;
- private int mNetworkType = -1;
- private SipWakeupTimer mTimer;
- private WifiManager.WifiLock mWifiLock;
- private boolean mSipOnWifiOnly;
-
- private SipKeepAliveProcessCallback mSipKeepAliveProcessCallback;
-
- private MyExecutor mExecutor = new MyExecutor();
-
- // SipProfile URI --> group
- private Map<String, SipSessionGroupExt> mSipGroups =
- new HashMap<String, SipSessionGroupExt>();
-
- // session ID --> session
- private Map<String, ISipSession> mPendingSessions =
- new HashMap<String, ISipSession>();
-
- private ConnectivityReceiver mConnectivityReceiver;
- private SipWakeLock mMyWakeLock;
- private int mKeepAliveInterval;
- private int mLastGoodKeepAliveInterval = DEFAULT_KEEPALIVE_INTERVAL;
-
- /**
- * Starts the SIP service. Do nothing if the SIP API is not supported on the
- * device.
- */
- public static void start(Context context) {
- if (SipManager.isApiSupported(context)) {
- ServiceManager.addService("sip", new SipService(context));
- context.sendBroadcast(new Intent(SipManager.ACTION_SIP_SERVICE_UP));
- if (DBG) slog("start:");
- }
- }
-
- private SipService(Context context) {
- if (DBG) log("SipService: started!");
- mContext = context;
- mConnectivityReceiver = new ConnectivityReceiver();
-
- mWifiLock = ((WifiManager)
- context.getSystemService(Context.WIFI_SERVICE))
- .createWifiLock(WifiManager.WIFI_MODE_FULL, TAG);
- mWifiLock.setReferenceCounted(false);
- mSipOnWifiOnly = SipManager.isSipWifiOnly(context);
-
- mMyWakeLock = new SipWakeLock((PowerManager)
- context.getSystemService(Context.POWER_SERVICE));
-
- mTimer = new SipWakeupTimer(context, mExecutor);
- }
-
- @Override
- public synchronized SipProfile[] getListOfProfiles() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.USE_SIP, null);
- boolean isCallerRadio = isCallerRadio();
- ArrayList<SipProfile> profiles = new ArrayList<SipProfile>();
- for (SipSessionGroupExt group : mSipGroups.values()) {
- if (isCallerRadio || isCallerCreator(group)) {
- profiles.add(group.getLocalProfile());
- }
- }
- return profiles.toArray(new SipProfile[profiles.size()]);
- }
-
- @Override
- public synchronized void open(SipProfile localProfile) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.USE_SIP, null);
- localProfile.setCallingUid(Binder.getCallingUid());
- try {
- createGroup(localProfile);
- } catch (SipException e) {
- loge("openToMakeCalls()", e);
- // TODO: how to send the exception back
- }
- }
-
- @Override
- public synchronized void open3(SipProfile localProfile,
- PendingIntent incomingCallPendingIntent,
- ISipSessionListener listener) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.USE_SIP, null);
- localProfile.setCallingUid(Binder.getCallingUid());
- if (incomingCallPendingIntent == null) {
- if (DBG) log("open3: incomingCallPendingIntent cannot be null; "
- + "the profile is not opened");
- return;
- }
- if (DBG) log("open3: " + localProfile.getUriString() + ": "
- + incomingCallPendingIntent + ": " + listener);
- try {
- SipSessionGroupExt group = createGroup(localProfile,
- incomingCallPendingIntent, listener);
- if (localProfile.getAutoRegistration()) {
- group.openToReceiveCalls();
- updateWakeLocks();
- }
- } catch (SipException e) {
- loge("open3:", e);
- // TODO: how to send the exception back
- }
- }
-
- 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);
- }
-
- @Override
- public synchronized void close(String localProfileUri) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.USE_SIP, null);
- SipSessionGroupExt group = mSipGroups.get(localProfileUri);
- if (group == null) return;
- if (!isCallerCreatorOrRadio(group)) {
- if (DBG) log("only creator or radio can close this profile");
- return;
- }
-
- group = mSipGroups.remove(localProfileUri);
- notifyProfileRemoved(group.getLocalProfile());
- group.close();
-
- updateWakeLocks();
- }
-
- @Override
- public synchronized boolean isOpened(String localProfileUri) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.USE_SIP, null);
- SipSessionGroupExt group = mSipGroups.get(localProfileUri);
- if (group == null) return false;
- if (isCallerCreatorOrRadio(group)) {
- return true;
- } else {
- if (DBG) log("only creator or radio can query on the profile");
- return false;
- }
- }
-
- @Override
- public synchronized boolean isRegistered(String localProfileUri) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.USE_SIP, null);
- SipSessionGroupExt group = mSipGroups.get(localProfileUri);
- if (group == null) return false;
- if (isCallerCreatorOrRadio(group)) {
- return group.isRegistered();
- } else {
- if (DBG) log("only creator or radio can query on the profile");
- return false;
- }
- }
-
- @Override
- public synchronized void setRegistrationListener(String localProfileUri,
- ISipSessionListener listener) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.USE_SIP, null);
- SipSessionGroupExt group = mSipGroups.get(localProfileUri);
- if (group == null) return;
- if (isCallerCreator(group)) {
- group.setListener(listener);
- } else {
- if (DBG) log("only creator can set listener on the profile");
- }
- }
-
- @Override
- public synchronized ISipSession createSession(SipProfile localProfile,
- ISipSessionListener listener) {
- if (DBG) log("createSession: profile" + localProfile);
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.USE_SIP, null);
- localProfile.setCallingUid(Binder.getCallingUid());
- if (mNetworkType == -1) {
- if (DBG) log("createSession: mNetworkType==-1 ret=null");
- return null;
- }
- try {
- SipSessionGroupExt group = createGroup(localProfile);
- return group.createSession(listener);
- } catch (SipException e) {
- if (DBG) loge("createSession;", e);
- return null;
- }
- }
-
- @Override
- public synchronized ISipSession getPendingSession(String callId) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.USE_SIP, null);
- if (callId == null) return null;
- return mPendingSessions.get(callId);
- }
-
- private String determineLocalIp() {
- try {
- DatagramSocket s = new DatagramSocket();
- s.connect(InetAddress.getByName("192.168.1.1"), 80);
- return s.getLocalAddress().getHostAddress();
- } catch (IOException e) {
- if (DBG) loge("determineLocalIp()", e);
- // dont do anything; there should be a connectivity change going
- return null;
- }
- }
-
- private SipSessionGroupExt createGroup(SipProfile localProfile)
- throws SipException {
- String key = localProfile.getUriString();
- SipSessionGroupExt group = mSipGroups.get(key);
- if (group == null) {
- 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;
- }
-
- private SipSessionGroupExt createGroup(SipProfile localProfile,
- PendingIntent incomingCallPendingIntent,
- ISipSessionListener listener) throws SipException {
- 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.setIncomingCallPendingIntent(incomingCallPendingIntent);
- group.setListener(listener);
- } else {
- group = new SipSessionGroupExt(localProfile,
- incomingCallPendingIntent, listener);
- mSipGroups.put(key, group);
- notifyProfileAdded(localProfile);
- }
- return group;
- }
-
- private void notifyProfileAdded(SipProfile localProfile) {
- if (DBG) log("notify: profile added: " + localProfile);
- Intent intent = new Intent(SipManager.ACTION_SIP_ADD_PHONE);
- intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
- mContext.sendBroadcast(intent);
- if (mSipGroups.size() == 1) {
- registerReceivers();
- }
- }
-
- private void notifyProfileRemoved(SipProfile localProfile) {
- if (DBG) log("notify: profile removed: " + localProfile);
- Intent intent = new Intent(SipManager.ACTION_SIP_REMOVE_PHONE);
- intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString());
- mContext.sendBroadcast(intent);
- if (mSipGroups.size() == 0) {
- unregisterReceivers();
- }
- }
-
- private void stopPortMappingMeasurement() {
- if (mSipKeepAliveProcessCallback != null) {
- mSipKeepAliveProcessCallback.stop();
- mSipKeepAliveProcessCallback = null;
- }
- }
-
- private void startPortMappingLifetimeMeasurement(
- SipProfile localProfile) {
- startPortMappingLifetimeMeasurement(localProfile,
- DEFAULT_MAX_KEEPALIVE_INTERVAL);
- }
-
- private void startPortMappingLifetimeMeasurement(
- SipProfile localProfile, int maxInterval) {
- if ((mSipKeepAliveProcessCallback == null)
- && (mKeepAliveInterval == -1)
- && isBehindNAT(mLocalIp)) {
- if (DBG) log("startPortMappingLifetimeMeasurement: profile="
- + localProfile.getUriString());
-
- int minInterval = mLastGoodKeepAliveInterval;
- if (minInterval >= maxInterval) {
- // If mLastGoodKeepAliveInterval also does not work, reset it
- // to the default min
- minInterval = mLastGoodKeepAliveInterval
- = DEFAULT_KEEPALIVE_INTERVAL;
- log(" reset min interval to " + minInterval);
- }
- mSipKeepAliveProcessCallback = new SipKeepAliveProcessCallback(
- localProfile, minInterval, maxInterval);
- mSipKeepAliveProcessCallback.start();
- }
- }
-
- private void restartPortMappingLifetimeMeasurement(
- SipProfile localProfile, int maxInterval) {
- stopPortMappingMeasurement();
- mKeepAliveInterval = -1;
- startPortMappingLifetimeMeasurement(localProfile, maxInterval);
- }
-
- private synchronized void addPendingSession(ISipSession session) {
- try {
- cleanUpPendingSessions();
- mPendingSessions.put(session.getCallId(), session);
- if (DBG) log("#pending sess=" + mPendingSessions.size());
- } catch (RemoteException e) {
- // should not happen with a local call
- loge("addPendingSession()", e);
- }
- }
-
- private void cleanUpPendingSessions() throws RemoteException {
- Map.Entry<String, ISipSession>[] entries =
- mPendingSessions.entrySet().toArray(
- new Map.Entry[mPendingSessions.size()]);
- for (Map.Entry<String, ISipSession> entry : entries) {
- if (entry.getValue().getState() != SipSession.State.INCOMING_CALL) {
- mPendingSessions.remove(entry.getKey());
- }
- }
- }
-
- private synchronized boolean callingSelf(SipSessionGroupExt ringingGroup,
- SipSessionGroup.SipSessionImpl ringingSession) {
- String callId = ringingSession.getCallId();
- for (SipSessionGroupExt group : mSipGroups.values()) {
- if ((group != ringingGroup) && group.containsSession(callId)) {
- if (DBG) log("call self: "
- + ringingSession.getLocalProfile().getUriString()
- + " -> " + group.getLocalProfile().getUriString());
- return true;
- }
- }
- return false;
- }
-
- private synchronized void onKeepAliveIntervalChanged() {
- for (SipSessionGroupExt group : mSipGroups.values()) {
- group.onKeepAliveIntervalChanged();
- }
- }
-
- private int getKeepAliveInterval() {
- return (mKeepAliveInterval < 0)
- ? mLastGoodKeepAliveInterval
- : mKeepAliveInterval;
- }
-
- private boolean isBehindNAT(String address) {
- try {
- // TODO: How is isBehindNAT used and why these constanst address:
- // 10.x.x.x | 192.168.x.x | 172.16.x.x .. 172.19.x.x
- byte[] d = InetAddress.getByName(address).getAddress();
- if ((d[0] == 10) ||
- (((0x000000FF & d[0]) == 172) &&
- ((0x000000F0 & d[1]) == 16)) ||
- (((0x000000FF & d[0]) == 192) &&
- ((0x000000FF & d[1]) == 168))) {
- return true;
- }
- } catch (UnknownHostException e) {
- loge("isBehindAT()" + address, e);
- }
- return false;
- }
-
- private class SipSessionGroupExt extends SipSessionAdapter {
- private static final String SSGE_TAG = "SipSessionGroupExt";
- private static final boolean SSGE_DBG = true;
- private SipSessionGroup mSipGroup;
- private PendingIntent mIncomingCallPendingIntent;
- private boolean mOpenedToReceiveCalls;
-
- private SipAutoReg mAutoRegistration =
- new SipAutoReg();
-
- public SipSessionGroupExt(SipProfile localProfile,
- PendingIntent incomingCallPendingIntent,
- ISipSessionListener listener) throws SipException {
- if (SSGE_DBG) log("SipSessionGroupExt: profile=" + localProfile);
- mSipGroup = new SipSessionGroup(duplicate(localProfile),
- localProfile.getPassword(), mTimer, mMyWakeLock);
- mIncomingCallPendingIntent = incomingCallPendingIntent;
- mAutoRegistration.setListener(listener);
- }
-
- public SipProfile getLocalProfile() {
- return mSipGroup.getLocalProfile();
- }
-
- public boolean containsSession(String callId) {
- return mSipGroup.containsSession(callId);
- }
-
- public void onKeepAliveIntervalChanged() {
- mAutoRegistration.onKeepAliveIntervalChanged();
- }
-
- // TODO: remove this method once SipWakeupTimer can better handle variety
- // of timeout values
- void setWakeupTimer(SipWakeupTimer timer) {
- mSipGroup.setWakeupTimer(timer);
- }
-
- private SipProfile duplicate(SipProfile p) {
- try {
- return new SipProfile.Builder(p).setPassword("*").build();
- } catch (Exception e) {
- loge("duplicate()", e);
- throw new RuntimeException("duplicate profile", e);
- }
- }
-
- public void setListener(ISipSessionListener listener) {
- mAutoRegistration.setListener(listener);
- }
-
- public void setIncomingCallPendingIntent(PendingIntent pIntent) {
- mIncomingCallPendingIntent = pIntent;
- }
-
- public void openToReceiveCalls() throws SipException {
- mOpenedToReceiveCalls = true;
- if (mNetworkType != -1) {
- mSipGroup.openToReceiveCalls(this);
- mAutoRegistration.start(mSipGroup);
- }
- if (SSGE_DBG) log("openToReceiveCalls: " + getUri() + ": "
- + mIncomingCallPendingIntent);
- }
-
- public void onConnectivityChanged(boolean connected)
- throws SipException {
- if (SSGE_DBG) {
- log("onConnectivityChanged: connected=" + connected + " uri="
- + getUri() + ": " + mIncomingCallPendingIntent);
- }
- mSipGroup.onConnectivityChanged();
- if (connected) {
- mSipGroup.reset();
- if (mOpenedToReceiveCalls) openToReceiveCalls();
- } else {
- mSipGroup.close();
- mAutoRegistration.stop();
- }
- }
-
- public void close() {
- mOpenedToReceiveCalls = false;
- mSipGroup.close();
- mAutoRegistration.stop();
- if (SSGE_DBG) log("close: " + getUri() + ": " + mIncomingCallPendingIntent);
- }
-
- public ISipSession createSession(ISipSessionListener listener) {
- if (SSGE_DBG) log("createSession");
- return mSipGroup.createSession(listener);
- }
-
- @Override
- public void onRinging(ISipSession s, SipProfile caller,
- String sessionDescription) {
- SipSessionGroup.SipSessionImpl session =
- (SipSessionGroup.SipSessionImpl) s;
- synchronized (SipService.this) {
- try {
- if (!isRegistered() || callingSelf(this, session)) {
- if (SSGE_DBG) log("onRinging: end notReg or self");
- session.endCall();
- return;
- }
-
- // send out incoming call broadcast
- addPendingSession(session);
- Intent intent = SipManager.createIncomingCallBroadcast(
- session.getCallId(), sessionDescription);
- if (SSGE_DBG) log("onRinging: uri=" + getUri() + ": "
- + caller.getUri() + ": " + session.getCallId()
- + " " + mIncomingCallPendingIntent);
- mIncomingCallPendingIntent.send(mContext,
- SipManager.INCOMING_CALL_RESULT_CODE, intent);
- } catch (PendingIntent.CanceledException e) {
- loge("onRinging: pendingIntent is canceled, drop incoming call", e);
- session.endCall();
- }
- }
- }
-
- @Override
- public void onError(ISipSession session, int errorCode,
- String message) {
- if (SSGE_DBG) log("onError: errorCode=" + errorCode + " desc="
- + SipErrorCode.toString(errorCode) + ": " + message);
- }
-
- public boolean isOpenedToReceiveCalls() {
- return mOpenedToReceiveCalls;
- }
-
- public boolean isRegistered() {
- return mAutoRegistration.isRegistered();
- }
-
- private String getUri() {
- return mSipGroup.getLocalProfileUri();
- }
-
- private void log(String s) {
- Rlog.d(SSGE_TAG, s);
- }
-
- private void loge(String s, Throwable t) {
- Rlog.e(SSGE_TAG, s, t);
- }
-
- }
-
- private class SipKeepAliveProcessCallback implements Runnable,
- SipSessionGroup.KeepAliveProcessCallback {
- private static final String SKAI_TAG = "SipKeepAliveProcessCallback";
- private static final boolean SKAI_DBG = true;
- private static final int MIN_INTERVAL = 5; // in seconds
- private static final int PASS_THRESHOLD = 10;
- private static final int NAT_MEASUREMENT_RETRY_INTERVAL = 120; // in seconds
- private SipProfile mLocalProfile;
- private SipSessionGroupExt mGroup;
- private SipSessionGroup.SipSessionImpl mSession;
- private int mMinInterval;
- private int mMaxInterval;
- private int mInterval;
- private int mPassCount;
-
- public SipKeepAliveProcessCallback(SipProfile localProfile,
- int minInterval, int maxInterval) {
- mMaxInterval = maxInterval;
- mMinInterval = minInterval;
- mLocalProfile = localProfile;
- }
-
- public void start() {
- synchronized (SipService.this) {
- if (mSession != null) {
- return;
- }
-
- mInterval = (mMaxInterval + mMinInterval) / 2;
- mPassCount = 0;
-
- // Don't start measurement if the interval is too small
- if (mInterval < DEFAULT_KEEPALIVE_INTERVAL || checkTermination()) {
- if (SKAI_DBG) log("start: measurement aborted; interval=[" +
- mMinInterval + "," + mMaxInterval + "]");
- return;
- }
-
- try {
- if (SKAI_DBG) log("start: interval=" + mInterval);
-
- mGroup = new SipSessionGroupExt(mLocalProfile, null, null);
- // TODO: remove this line once SipWakeupTimer can better handle
- // variety of timeout values
- mGroup.setWakeupTimer(new SipWakeupTimer(mContext, mExecutor));
-
- mSession = (SipSessionGroup.SipSessionImpl)
- mGroup.createSession(null);
- mSession.startKeepAliveProcess(mInterval, this);
- } catch (Throwable t) {
- onError(SipErrorCode.CLIENT_ERROR, t.toString());
- }
- }
- }
-
- public void stop() {
- synchronized (SipService.this) {
- if (mSession != null) {
- mSession.stopKeepAliveProcess();
- mSession = null;
- }
- if (mGroup != null) {
- mGroup.close();
- mGroup = null;
- }
- mTimer.cancel(this);
- if (SKAI_DBG) log("stop");
- }
- }
-
- private void restart() {
- synchronized (SipService.this) {
- // Return immediately if the measurement process is stopped
- if (mSession == null) return;
-
- if (SKAI_DBG) log("restart: interval=" + mInterval);
- try {
- mSession.stopKeepAliveProcess();
- mPassCount = 0;
- mSession.startKeepAliveProcess(mInterval, this);
- } catch (SipException e) {
- loge("restart", e);
- }
- }
- }
-
- private boolean checkTermination() {
- return ((mMaxInterval - mMinInterval) < MIN_INTERVAL);
- }
-
- // SipSessionGroup.KeepAliveProcessCallback
- @Override
- public void onResponse(boolean portChanged) {
- synchronized (SipService.this) {
- if (!portChanged) {
- if (++mPassCount != PASS_THRESHOLD) return;
- // update the interval, since the current interval is good to
- // keep the port mapping.
- if (mKeepAliveInterval > 0) {
- mLastGoodKeepAliveInterval = mKeepAliveInterval;
- }
- mKeepAliveInterval = mMinInterval = mInterval;
- if (SKAI_DBG) {
- log("onResponse: portChanged=" + portChanged + " mKeepAliveInterval="
- + mKeepAliveInterval);
- }
- onKeepAliveIntervalChanged();
- } else {
- // Since the rport is changed, shorten the interval.
- mMaxInterval = mInterval;
- }
- if (checkTermination()) {
- // update mKeepAliveInterval and stop measurement.
- stop();
- // If all the measurements failed, we still set it to
- // mMinInterval; If mMinInterval still doesn't work, a new
- // measurement with min interval=DEFAULT_KEEPALIVE_INTERVAL
- // will be conducted.
- mKeepAliveInterval = mMinInterval;
- if (SKAI_DBG) {
- log("onResponse: checkTermination mKeepAliveInterval="
- + mKeepAliveInterval);
- }
- } else {
- // calculate the new interval and continue.
- mInterval = (mMaxInterval + mMinInterval) / 2;
- if (SKAI_DBG) {
- log("onResponse: mKeepAliveInterval=" + mKeepAliveInterval
- + ", new mInterval=" + mInterval);
- }
- restart();
- }
- }
- }
-
- // SipSessionGroup.KeepAliveProcessCallback
- @Override
- public void onError(int errorCode, String description) {
- if (SKAI_DBG) loge("onError: errorCode=" + errorCode + " desc=" + description);
- restartLater();
- }
-
- // timeout handler
- @Override
- public void run() {
- mTimer.cancel(this);
- restart();
- }
-
- private void restartLater() {
- synchronized (SipService.this) {
- int interval = NAT_MEASUREMENT_RETRY_INTERVAL;
- mTimer.cancel(this);
- mTimer.set(interval * 1000, this);
- }
- }
-
- private void log(String s) {
- Rlog.d(SKAI_TAG, s);
- }
-
- private void loge(String s) {
- Rlog.d(SKAI_TAG, s);
- }
-
- private void loge(String s, Throwable t) {
- Rlog.d(SKAI_TAG, s, t);
- }
- }
-
- private class SipAutoReg extends SipSessionAdapter
- implements Runnable, SipSessionGroup.KeepAliveProcessCallback {
- private String SAR_TAG;
- private static final boolean SAR_DBG = true;
- private static final int MIN_KEEPALIVE_SUCCESS_COUNT = 10;
-
- private SipSessionGroup.SipSessionImpl mSession;
- private SipSessionGroup.SipSessionImpl mKeepAliveSession;
- private SipSessionListenerProxy mProxy = new SipSessionListenerProxy();
- private int mBackoff = 1;
- private boolean mRegistered;
- private long mExpiryTime;
- private int mErrorCode;
- private String mErrorMessage;
- private boolean mRunning = false;
-
- private int mKeepAliveSuccessCount = 0;
-
- public void start(SipSessionGroup group) {
- if (!mRunning) {
- mRunning = true;
- mBackoff = 1;
- mSession = (SipSessionGroup.SipSessionImpl)
- group.createSession(this);
- // return right away if no active network connection.
- if (mSession == null) return;
-
- // start unregistration to clear up old registration at server
- // TODO: when rfc5626 is deployed, use reg-id and sip.instance
- // in registration to avoid adding duplicate entries to server
- mMyWakeLock.acquire(mSession);
- mSession.unregister();
- SAR_TAG = "SipAutoReg:" + mSession.getLocalProfile().getUriString();
- if (SAR_DBG) log("start: group=" + group);
- }
- }
-
- private void startKeepAliveProcess(int interval) {
- if (SAR_DBG) log("startKeepAliveProcess: interval=" + interval);
- if (mKeepAliveSession == null) {
- mKeepAliveSession = mSession.duplicate();
- } else {
- mKeepAliveSession.stopKeepAliveProcess();
- }
- try {
- mKeepAliveSession.startKeepAliveProcess(interval, this);
- } catch (SipException e) {
- loge("startKeepAliveProcess: interval=" + interval, e);
- }
- }
-
- private void stopKeepAliveProcess() {
- if (mKeepAliveSession != null) {
- mKeepAliveSession.stopKeepAliveProcess();
- mKeepAliveSession = null;
- }
- mKeepAliveSuccessCount = 0;
- }
-
- // SipSessionGroup.KeepAliveProcessCallback
- @Override
- public void onResponse(boolean portChanged) {
- synchronized (SipService.this) {
- if (portChanged) {
- int interval = getKeepAliveInterval();
- if (mKeepAliveSuccessCount < MIN_KEEPALIVE_SUCCESS_COUNT) {
- if (SAR_DBG) {
- log("onResponse: keepalive doesn't work with interval "
- + interval + ", past success count="
- + mKeepAliveSuccessCount);
- }
- if (interval > DEFAULT_KEEPALIVE_INTERVAL) {
- restartPortMappingLifetimeMeasurement(
- mSession.getLocalProfile(), interval);
- mKeepAliveSuccessCount = 0;
- }
- } else {
- if (SAR_DBG) {
- log("keep keepalive going with interval "
- + interval + ", past success count="
- + mKeepAliveSuccessCount);
- }
- mKeepAliveSuccessCount /= 2;
- }
- } else {
- // Start keep-alive interval measurement on the first
- // successfully kept-alive SipSessionGroup
- startPortMappingLifetimeMeasurement(
- mSession.getLocalProfile());
- mKeepAliveSuccessCount++;
- }
-
- if (!mRunning || !portChanged) return;
-
- // The keep alive process is stopped when port is changed;
- // Nullify the session so that the process can be restarted
- // again when the re-registration is done
- mKeepAliveSession = null;
-
- // Acquire wake lock for the registration process. The
- // lock will be released when registration is complete.
- mMyWakeLock.acquire(mSession);
- mSession.register(EXPIRY_TIME);
- }
- }
-
- // SipSessionGroup.KeepAliveProcessCallback
- @Override
- public void onError(int errorCode, String description) {
- if (SAR_DBG) {
- loge("onError: errorCode=" + errorCode + " desc=" + description);
- }
- onResponse(true); // re-register immediately
- }
-
- public void stop() {
- if (!mRunning) return;
- mRunning = false;
- mMyWakeLock.release(mSession);
- if (mSession != null) {
- mSession.setListener(null);
- if (mNetworkType != -1 && mRegistered) mSession.unregister();
- }
-
- mTimer.cancel(this);
- stopKeepAliveProcess();
-
- mRegistered = false;
- setListener(mProxy.getListener());
- }
-
- public void onKeepAliveIntervalChanged() {
- if (mKeepAliveSession != null) {
- int newInterval = getKeepAliveInterval();
- if (SAR_DBG) {
- log("onKeepAliveIntervalChanged: interval=" + newInterval);
- }
- mKeepAliveSuccessCount = 0;
- startKeepAliveProcess(newInterval);
- }
- }
-
- public void setListener(ISipSessionListener listener) {
- synchronized (SipService.this) {
- mProxy.setListener(listener);
-
- try {
- int state = (mSession == null)
- ? SipSession.State.READY_TO_CALL
- : mSession.getState();
- if ((state == SipSession.State.REGISTERING)
- || (state == SipSession.State.DEREGISTERING)) {
- mProxy.onRegistering(mSession);
- } else if (mRegistered) {
- int duration = (int)
- (mExpiryTime - SystemClock.elapsedRealtime());
- mProxy.onRegistrationDone(mSession, duration);
- } else if (mErrorCode != SipErrorCode.NO_ERROR) {
- if (mErrorCode == SipErrorCode.TIME_OUT) {
- mProxy.onRegistrationTimeout(mSession);
- } else {
- mProxy.onRegistrationFailed(mSession, mErrorCode,
- mErrorMessage);
- }
- } else if (mNetworkType == -1) {
- 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) {
- loge("setListener: ", t);
- }
- }
- }
-
- public boolean isRegistered() {
- return mRegistered;
- }
-
- // timeout handler: re-register
- @Override
- public void run() {
- synchronized (SipService.this) {
- if (!mRunning) return;
-
- mErrorCode = SipErrorCode.NO_ERROR;
- mErrorMessage = null;
- if (SAR_DBG) log("run: registering");
- if (mNetworkType != -1) {
- mMyWakeLock.acquire(mSession);
- mSession.register(EXPIRY_TIME);
- }
- }
- }
-
- private void restart(int duration) {
- if (SAR_DBG) log("restart: duration=" + duration + "s later.");
- mTimer.cancel(this);
- mTimer.set(duration * 1000, this);
- }
-
- private int backoffDuration() {
- int duration = SHORT_EXPIRY_TIME * mBackoff;
- if (duration > 3600) {
- duration = 3600;
- } else {
- mBackoff *= 2;
- }
- return duration;
- }
-
- @Override
- public void onRegistering(ISipSession session) {
- if (SAR_DBG) log("onRegistering: " + session);
- synchronized (SipService.this) {
- if (notCurrentSession(session)) return;
-
- mRegistered = false;
- mProxy.onRegistering(session);
- }
- }
-
- private boolean notCurrentSession(ISipSession session) {
- if (session != mSession) {
- ((SipSessionGroup.SipSessionImpl) session).setListener(null);
- mMyWakeLock.release(session);
- return true;
- }
- return !mRunning;
- }
-
- @Override
- public void onRegistrationDone(ISipSession session, int duration) {
- if (SAR_DBG) log("onRegistrationDone: " + session);
- synchronized (SipService.this) {
- if (notCurrentSession(session)) return;
-
- mProxy.onRegistrationDone(session, duration);
-
- if (duration > 0) {
- mExpiryTime = SystemClock.elapsedRealtime()
- + (duration * 1000);
-
- if (!mRegistered) {
- mRegistered = true;
- // allow some overlap to avoid call drop during renew
- duration -= MIN_EXPIRY_TIME;
- if (duration < MIN_EXPIRY_TIME) {
- duration = MIN_EXPIRY_TIME;
- }
- restart(duration);
-
- SipProfile localProfile = mSession.getLocalProfile();
- if ((mKeepAliveSession == null) && (isBehindNAT(mLocalIp)
- || localProfile.getSendKeepAlive())) {
- startKeepAliveProcess(getKeepAliveInterval());
- }
- }
- mMyWakeLock.release(session);
- } else {
- mRegistered = false;
- mExpiryTime = -1L;
- if (SAR_DBG) log("Refresh registration immediately");
- run();
- }
- }
- }
-
- @Override
- public void onRegistrationFailed(ISipSession session, int errorCode,
- String message) {
- if (SAR_DBG) log("onRegistrationFailed: " + session + ": "
- + SipErrorCode.toString(errorCode) + ": " + message);
- synchronized (SipService.this) {
- if (notCurrentSession(session)) return;
-
- switch (errorCode) {
- case SipErrorCode.INVALID_CREDENTIALS:
- case SipErrorCode.SERVER_UNREACHABLE:
- if (SAR_DBG) log(" pause auto-registration");
- stop();
- break;
- default:
- restartLater();
- }
-
- mErrorCode = errorCode;
- mErrorMessage = message;
- mProxy.onRegistrationFailed(session, errorCode, message);
- mMyWakeLock.release(session);
- }
- }
-
- @Override
- public void onRegistrationTimeout(ISipSession session) {
- if (SAR_DBG) log("onRegistrationTimeout: " + session);
- synchronized (SipService.this) {
- if (notCurrentSession(session)) return;
-
- mErrorCode = SipErrorCode.TIME_OUT;
- mProxy.onRegistrationTimeout(session);
- restartLater();
- mMyWakeLock.release(session);
- }
- }
-
- private void restartLater() {
- if (SAR_DBG) loge("restartLater");
- mRegistered = false;
- restart(backoffDuration());
- }
-
- private void log(String s) {
- Rlog.d(SAR_TAG, s);
- }
-
- private void loge(String s) {
- Rlog.e(SAR_TAG, s);
- }
-
- private void loge(String s, Throwable e) {
- Rlog.e(SAR_TAG, s, e);
- }
- }
-
- private class ConnectivityReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- Bundle bundle = intent.getExtras();
- if (bundle != null) {
- final NetworkInfo info = (NetworkInfo)
- bundle.get(ConnectivityManager.EXTRA_NETWORK_INFO);
-
- // Run the handler in MyExecutor to be protected by wake lock
- mExecutor.execute(new Runnable() {
- @Override
- public void run() {
- onConnectivityChanged(info);
- }
- });
- }
- }
- }
-
- private void registerReceivers() {
- mContext.registerReceiver(mConnectivityReceiver,
- new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
- if (DBG) log("registerReceivers:");
- }
-
- private void unregisterReceivers() {
- mContext.unregisterReceiver(mConnectivityReceiver);
- if (DBG) log("unregisterReceivers:");
-
- // Reset variables maintained by ConnectivityReceiver.
- mWifiLock.release();
- mNetworkType = -1;
- }
-
- private void updateWakeLocks() {
- for (SipSessionGroupExt group : mSipGroups.values()) {
- if (group.isOpenedToReceiveCalls()) {
- // Also grab the WifiLock when we are disconnected, so the
- // system will keep trying to reconnect. It will be released
- // when the system eventually connects to something else.
- if (mNetworkType == ConnectivityManager.TYPE_WIFI || mNetworkType == -1) {
- mWifiLock.acquire();
- } else {
- mWifiLock.release();
- }
- return;
- }
- }
- mWifiLock.release();
- mMyWakeLock.reset(); // in case there's a leak
- }
-
- private synchronized void onConnectivityChanged(NetworkInfo info) {
- // We only care about the default network, and getActiveNetworkInfo()
- // is the only way to distinguish them. However, as broadcasts are
- // delivered asynchronously, we might miss DISCONNECTED events from
- // getActiveNetworkInfo(), which is critical to our SIP stack. To
- // solve this, if it is a DISCONNECTED event to our current network,
- // respect it. Otherwise get a new one from getActiveNetworkInfo().
- if (info == null || info.isConnected() || info.getType() != mNetworkType) {
- ConnectivityManager cm = (ConnectivityManager)
- mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- info = cm.getActiveNetworkInfo();
- }
-
- // Some devices limit SIP on Wi-Fi. In this case, if we are not on
- // Wi-Fi, treat it as a DISCONNECTED event.
- int networkType = (info != null && info.isConnected()) ? info.getType() : -1;
- if (mSipOnWifiOnly && networkType != ConnectivityManager.TYPE_WIFI) {
- networkType = -1;
- }
-
- // Ignore the event if the current active network is not changed.
- if (mNetworkType == networkType) {
- // TODO: Maybe we need to send seq/generation number
- return;
- }
- if (DBG) {
- log("onConnectivityChanged: " + mNetworkType +
- " -> " + networkType);
- }
-
- try {
- if (mNetworkType != -1) {
- mLocalIp = null;
- stopPortMappingMeasurement();
- for (SipSessionGroupExt group : mSipGroups.values()) {
- group.onConnectivityChanged(false);
- }
- }
- mNetworkType = networkType;
-
- if (mNetworkType != -1) {
- mLocalIp = determineLocalIp();
- mKeepAliveInterval = -1;
- mLastGoodKeepAliveInterval = DEFAULT_KEEPALIVE_INTERVAL;
- for (SipSessionGroupExt group : mSipGroups.values()) {
- group.onConnectivityChanged(true);
- }
- }
- updateWakeLocks();
- } catch (SipException e) {
- loge("onConnectivityChanged()", e);
- }
- }
-
- private static Looper createLooper() {
- HandlerThread thread = new HandlerThread("SipService.Executor");
- thread.start();
- return thread.getLooper();
- }
-
- // Executes immediate tasks in a single thread.
- // Hold/release wake lock for running tasks
- private class MyExecutor extends Handler implements Executor {
- MyExecutor() {
- super(createLooper());
- }
-
- @Override
- public void execute(Runnable task) {
- mMyWakeLock.acquire(task);
- Message.obtain(this, 0/* don't care */, task).sendToTarget();
- }
-
- @Override
- public void handleMessage(Message msg) {
- if (msg.obj instanceof Runnable) {
- executeInternal((Runnable) msg.obj);
- } else {
- if (DBG) log("handleMessage: not Runnable ignore msg=" + msg);
- }
- }
-
- private void executeInternal(Runnable task) {
- try {
- task.run();
- } catch (Throwable t) {
- loge("run task: " + task, t);
- } finally {
- mMyWakeLock.release(task);
- }
- }
- }
-
- private void log(String s) {
- Rlog.d(TAG, s);
- }
-
- private static void slog(String s) {
- Rlog.d(TAG, s);
- }
-
- private void loge(String s, Throwable e) {
- Rlog.e(TAG, s, e);
- }
-}
diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java
deleted file mode 100644
index e820f35..0000000
--- a/voip/java/com/android/server/sip/SipSessionGroup.java
+++ /dev/null
@@ -1,1863 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.sip;
-
-import gov.nist.javax.sip.clientauthutils.AccountManager;
-import gov.nist.javax.sip.clientauthutils.UserCredentials;
-import gov.nist.javax.sip.header.ProxyAuthenticate;
-import gov.nist.javax.sip.header.ReferTo;
-import gov.nist.javax.sip.header.SIPHeaderNames;
-import gov.nist.javax.sip.header.StatusLine;
-import gov.nist.javax.sip.header.WWWAuthenticate;
-import gov.nist.javax.sip.header.extensions.ReferredByHeader;
-import gov.nist.javax.sip.header.extensions.ReplacesHeader;
-import gov.nist.javax.sip.message.SIPMessage;
-import gov.nist.javax.sip.message.SIPResponse;
-
-import android.net.sip.ISipSession;
-import android.net.sip.ISipSessionListener;
-import android.net.sip.SipErrorCode;
-import android.net.sip.SipProfile;
-import android.net.sip.SipSession;
-import android.net.sip.SipSessionAdapter;
-import android.text.TextUtils;
-import android.telephony.Rlog;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.net.DatagramSocket;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.text.ParseException;
-import java.util.EventObject;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-
-import javax.sip.ClientTransaction;
-import javax.sip.Dialog;
-import javax.sip.DialogTerminatedEvent;
-import javax.sip.IOExceptionEvent;
-import javax.sip.ObjectInUseException;
-import javax.sip.RequestEvent;
-import javax.sip.ResponseEvent;
-import javax.sip.ServerTransaction;
-import javax.sip.SipException;
-import javax.sip.SipFactory;
-import javax.sip.SipListener;
-import javax.sip.SipProvider;
-import javax.sip.SipStack;
-import javax.sip.TimeoutEvent;
-import javax.sip.Transaction;
-import javax.sip.TransactionTerminatedEvent;
-import javax.sip.address.Address;
-import javax.sip.address.SipURI;
-import javax.sip.header.CSeqHeader;
-import javax.sip.header.ContactHeader;
-import javax.sip.header.ExpiresHeader;
-import javax.sip.header.FromHeader;
-import javax.sip.header.HeaderAddress;
-import javax.sip.header.MinExpiresHeader;
-import javax.sip.header.ReferToHeader;
-import javax.sip.header.ViaHeader;
-import javax.sip.message.Message;
-import javax.sip.message.Request;
-import javax.sip.message.Response;
-
-
-/**
- * Manages {@link ISipSession}'s for a SIP account.
- */
-class SipSessionGroup implements SipListener {
- private static final String TAG = "SipSession";
- private static final boolean DBG = false;
- private static final boolean DBG_PING = false;
- private static final String ANONYMOUS = "anonymous";
- // Limit the size of thread pool to 1 for the order issue when the phone is
- // waken up from sleep and there are many packets to be processed in the SIP
- // stack. Note: The default thread pool size in NIST SIP stack is -1 which is
- // unlimited.
- private static final String THREAD_POOL_SIZE = "1";
- private static final int EXPIRY_TIME = 3600; // in seconds
- private static final int CANCEL_CALL_TIMER = 3; // in seconds
- private static final int END_CALL_TIMER = 3; // in seconds
- private static final int KEEPALIVE_TIMEOUT = 5; // in seconds
- private static final int INCALL_KEEPALIVE_INTERVAL = 10; // in seconds
- private static final long WAKE_LOCK_HOLDING_TIME = 500; // in milliseconds
-
- private static final EventObject DEREGISTER = new EventObject("Deregister");
- private static final EventObject END_CALL = new EventObject("End call");
-
- private final SipProfile mLocalProfile;
- private final String mPassword;
-
- private SipStack mSipStack;
- private SipHelper mSipHelper;
-
- // session that processes INVITE requests
- private SipSessionImpl mCallReceiverSession;
- private String mLocalIp;
-
- private SipWakeupTimer mWakeupTimer;
- private SipWakeLock mWakeLock;
-
- // call-id-to-SipSession map
- private Map<String, SipSessionImpl> mSessionMap =
- new HashMap<String, SipSessionImpl>();
-
- // external address observed from any response
- private String mExternalIp;
- private int mExternalPort;
-
- /**
- * @param profile the local profile with password crossed out
- * @param password the password of the profile
- * @throws SipException if cannot assign requested address
- */
- public SipSessionGroup(SipProfile profile, String password,
- SipWakeupTimer timer, SipWakeLock wakeLock) throws SipException {
- mLocalProfile = profile;
- mPassword = password;
- mWakeupTimer = timer;
- mWakeLock = wakeLock;
- reset();
- }
-
- // TODO: remove this method once SipWakeupTimer can better handle variety
- // of timeout values
- void setWakeupTimer(SipWakeupTimer timer) {
- mWakeupTimer = timer;
- }
-
- synchronized void reset() throws SipException {
- Properties properties = new Properties();
-
- String protocol = mLocalProfile.getProtocol();
- int port = mLocalProfile.getPort();
- String server = mLocalProfile.getProxyAddress();
-
- if (!TextUtils.isEmpty(server)) {
- properties.setProperty("javax.sip.OUTBOUND_PROXY",
- server + ':' + port + '/' + protocol);
- } else {
- server = mLocalProfile.getSipDomain();
- }
- if (server.startsWith("[") && server.endsWith("]")) {
- server = server.substring(1, server.length() - 1);
- }
-
- String local = null;
- try {
- for (InetAddress remote : InetAddress.getAllByName(server)) {
- DatagramSocket socket = new DatagramSocket();
- socket.connect(remote, port);
- if (socket.isConnected()) {
- local = socket.getLocalAddress().getHostAddress();
- port = socket.getLocalPort();
- socket.close();
- break;
- }
- socket.close();
- }
- } catch (Exception e) {
- // ignore.
- }
- if (local == null) {
- // We are unable to reach the server. Just bail out.
- return;
- }
-
- close();
- mLocalIp = local;
-
- properties.setProperty("javax.sip.STACK_NAME", getStackName());
- properties.setProperty(
- "gov.nist.javax.sip.THREAD_POOL_SIZE", THREAD_POOL_SIZE);
- mSipStack = SipFactory.getInstance().createSipStack(properties);
- try {
- SipProvider provider = mSipStack.createSipProvider(
- mSipStack.createListeningPoint(local, port, protocol));
- provider.addSipListener(this);
- mSipHelper = new SipHelper(mSipStack, provider);
- } catch (SipException e) {
- throw e;
- } catch (Exception e) {
- throw new SipException("failed to initialize SIP stack", e);
- }
-
- if (DBG) log("reset: start stack for " + mLocalProfile.getUriString());
- mSipStack.start();
- }
-
- synchronized void onConnectivityChanged() {
- SipSessionImpl[] ss = mSessionMap.values().toArray(
- new SipSessionImpl[mSessionMap.size()]);
- // Iterate on the copied array instead of directly on mSessionMap to
- // avoid ConcurrentModificationException being thrown when
- // SipSessionImpl removes itself from mSessionMap in onError() in the
- // following loop.
- for (SipSessionImpl s : ss) {
- s.onError(SipErrorCode.DATA_CONNECTION_LOST,
- "data connection lost");
- }
- }
-
- synchronized void resetExternalAddress() {
- if (DBG) {
- log("resetExternalAddress: " + mSipStack);
- }
- mExternalIp = null;
- mExternalPort = 0;
- }
-
- public SipProfile getLocalProfile() {
- return mLocalProfile;
- }
-
- public String getLocalProfileUri() {
- return mLocalProfile.getUriString();
- }
-
- private String getStackName() {
- return "stack" + System.currentTimeMillis();
- }
-
- public synchronized void close() {
- if (DBG) log("close: " + mLocalProfile.getUriString());
- onConnectivityChanged();
- mSessionMap.clear();
- closeToNotReceiveCalls();
- if (mSipStack != null) {
- mSipStack.stop();
- mSipStack = null;
- mSipHelper = null;
- }
- resetExternalAddress();
- }
-
- public synchronized boolean isClosed() {
- return (mSipStack == null);
- }
-
- // For internal use, require listener not to block in callbacks.
- public synchronized void openToReceiveCalls(ISipSessionListener listener) {
- if (mCallReceiverSession == null) {
- mCallReceiverSession = new SipSessionCallReceiverImpl(listener);
- } else {
- mCallReceiverSession.setListener(listener);
- }
- }
-
- public synchronized void closeToNotReceiveCalls() {
- mCallReceiverSession = null;
- }
-
- public ISipSession createSession(ISipSessionListener listener) {
- return (isClosed() ? null : new SipSessionImpl(listener));
- }
-
- synchronized boolean containsSession(String callId) {
- return mSessionMap.containsKey(callId);
- }
-
- private synchronized SipSessionImpl getSipSession(EventObject event) {
- String key = SipHelper.getCallId(event);
- SipSessionImpl session = mSessionMap.get(key);
- if ((session != null) && isLoggable(session)) {
- if (DBG) log("getSipSession: event=" + key);
- if (DBG) log("getSipSession: active sessions:");
- for (String k : mSessionMap.keySet()) {
- if (DBG) log("getSipSession: ..." + k + ": " + mSessionMap.get(k));
- }
- }
- return ((session != null) ? session : mCallReceiverSession);
- }
-
- private synchronized void addSipSession(SipSessionImpl newSession) {
- removeSipSession(newSession);
- String key = newSession.getCallId();
- mSessionMap.put(key, newSession);
- if (isLoggable(newSession)) {
- if (DBG) log("addSipSession: key='" + key + "'");
- for (String k : mSessionMap.keySet()) {
- if (DBG) log("addSipSession: " + k + ": " + mSessionMap.get(k));
- }
- }
- }
-
- private synchronized void removeSipSession(SipSessionImpl session) {
- if (session == mCallReceiverSession) return;
- String key = session.getCallId();
- SipSessionImpl s = mSessionMap.remove(key);
- // sanity check
- if ((s != null) && (s != session)) {
- if (DBG) log("removeSession: " + session + " is not associated with key '"
- + key + "'");
- mSessionMap.put(key, s);
- for (Map.Entry<String, SipSessionImpl> entry
- : mSessionMap.entrySet()) {
- if (entry.getValue() == s) {
- key = entry.getKey();
- mSessionMap.remove(key);
- }
- }
- }
-
- if ((s != null) && isLoggable(s)) {
- if (DBG) log("removeSession: " + session + " @key '" + key + "'");
- for (String k : mSessionMap.keySet()) {
- if (DBG) log("removeSession: " + k + ": " + mSessionMap.get(k));
- }
- }
- }
-
- @Override
- public void processRequest(final RequestEvent event) {
- if (isRequestEvent(Request.INVITE, event)) {
- if (DBG) log("processRequest: mWakeLock.acquire got INVITE, thread:"
- + Thread.currentThread());
- // Acquire a wake lock and keep it for WAKE_LOCK_HOLDING_TIME;
- // should be large enough to bring up the app.
- mWakeLock.acquire(WAKE_LOCK_HOLDING_TIME);
- }
- process(event);
- }
-
- @Override
- public void processResponse(ResponseEvent event) {
- process(event);
- }
-
- @Override
- public void processIOException(IOExceptionEvent event) {
- process(event);
- }
-
- @Override
- public void processTimeout(TimeoutEvent event) {
- process(event);
- }
-
- @Override
- public void processTransactionTerminated(TransactionTerminatedEvent event) {
- process(event);
- }
-
- @Override
- public void processDialogTerminated(DialogTerminatedEvent event) {
- process(event);
- }
-
- private synchronized void process(EventObject event) {
- SipSessionImpl session = getSipSession(event);
- try {
- boolean isLoggable = isLoggable(session, event);
- boolean processed = (session != null) && session.process(event);
- if (isLoggable && processed) {
- log("process: event new state after: "
- + SipSession.State.toString(session.mState));
- }
- } catch (Throwable e) {
- loge("process: error event=" + event, getRootCause(e));
- session.onError(e);
- }
- }
-
- private String extractContent(Message message) {
- // Currently we do not support secure MIME bodies.
- byte[] bytes = message.getRawContent();
- if (bytes != null) {
- try {
- if (message instanceof SIPMessage) {
- return ((SIPMessage) message).getMessageContent();
- } else {
- return new String(bytes, "UTF-8");
- }
- } catch (UnsupportedEncodingException e) {
- }
- }
- return null;
- }
-
- private void extractExternalAddress(ResponseEvent evt) {
- Response response = evt.getResponse();
- ViaHeader viaHeader = (ViaHeader)(response.getHeader(
- SIPHeaderNames.VIA));
- if (viaHeader == null) return;
- int rport = viaHeader.getRPort();
- String externalIp = viaHeader.getReceived();
- if ((rport > 0) && (externalIp != null)) {
- mExternalIp = externalIp;
- mExternalPort = rport;
- if (DBG) {
- log("extractExternalAddress: external addr " + externalIp + ":" + rport
- + " on " + mSipStack);
- }
- }
- }
-
- private Throwable getRootCause(Throwable exception) {
- Throwable cause = exception.getCause();
- while (cause != null) {
- exception = cause;
- cause = exception.getCause();
- }
- return exception;
- }
-
- private SipSessionImpl createNewSession(RequestEvent event,
- ISipSessionListener listener, ServerTransaction transaction,
- int newState) throws SipException {
- SipSessionImpl newSession = new SipSessionImpl(listener);
- newSession.mServerTransaction = transaction;
- newSession.mState = newState;
- newSession.mDialog = newSession.mServerTransaction.getDialog();
- newSession.mInviteReceived = event;
- newSession.mPeerProfile = createPeerProfile((HeaderAddress)
- event.getRequest().getHeader(FromHeader.NAME));
- newSession.mPeerSessionDescription =
- extractContent(event.getRequest());
- return newSession;
- }
-
- private class SipSessionCallReceiverImpl extends SipSessionImpl {
- private static final String SSCRI_TAG = "SipSessionCallReceiverImpl";
- private static final boolean SSCRI_DBG = true;
-
- public SipSessionCallReceiverImpl(ISipSessionListener listener) {
- super(listener);
- }
-
- private int processInviteWithReplaces(RequestEvent event,
- ReplacesHeader replaces) {
- String callId = replaces.getCallId();
- SipSessionImpl session = mSessionMap.get(callId);
- if (session == null) {
- return Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST;
- }
-
- Dialog dialog = session.mDialog;
- if (dialog == null) return Response.DECLINE;
-
- if (!dialog.getLocalTag().equals(replaces.getToTag()) ||
- !dialog.getRemoteTag().equals(replaces.getFromTag())) {
- // No match is found, returns 481.
- return Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST;
- }
-
- ReferredByHeader referredBy = (ReferredByHeader) event.getRequest()
- .getHeader(ReferredByHeader.NAME);
- if ((referredBy == null) ||
- !dialog.getRemoteParty().equals(referredBy.getAddress())) {
- return Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST;
- }
- return Response.OK;
- }
-
- private void processNewInviteRequest(RequestEvent event)
- throws SipException {
- ReplacesHeader replaces = (ReplacesHeader) event.getRequest()
- .getHeader(ReplacesHeader.NAME);
- SipSessionImpl newSession = null;
- if (replaces != null) {
- int response = processInviteWithReplaces(event, replaces);
- if (SSCRI_DBG) {
- log("processNewInviteRequest: " + replaces
- + " response=" + response);
- }
- if (response == Response.OK) {
- SipSessionImpl replacedSession =
- mSessionMap.get(replaces.getCallId());
- // got INVITE w/ replaces request.
- newSession = createNewSession(event,
- replacedSession.mProxy.getListener(),
- mSipHelper.getServerTransaction(event),
- SipSession.State.INCOMING_CALL);
- newSession.mProxy.onCallTransferring(newSession,
- newSession.mPeerSessionDescription);
- } else {
- mSipHelper.sendResponse(event, response);
- }
- } else {
- // New Incoming call.
- newSession = createNewSession(event, mProxy,
- mSipHelper.sendRinging(event, generateTag()),
- SipSession.State.INCOMING_CALL);
- mProxy.onRinging(newSession, newSession.mPeerProfile,
- newSession.mPeerSessionDescription);
- }
- if (newSession != null) addSipSession(newSession);
- }
-
- @Override
- public boolean process(EventObject evt) throws SipException {
- if (isLoggable(this, evt)) log("process: " + this + ": "
- + SipSession.State.toString(mState) + ": processing "
- + logEvt(evt));
- if (isRequestEvent(Request.INVITE, evt)) {
- processNewInviteRequest((RequestEvent) evt);
- return true;
- } else if (isRequestEvent(Request.OPTIONS, evt)) {
- mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
- return true;
- } else {
- return false;
- }
- }
-
- private void log(String s) {
- Rlog.d(SSCRI_TAG, s);
- }
- }
-
- static interface KeepAliveProcessCallback {
- /** Invoked when the response of keeping alive comes back. */
- void onResponse(boolean portChanged);
- void onError(int errorCode, String description);
- }
-
- class SipSessionImpl extends ISipSession.Stub {
- private static final String SSI_TAG = "SipSessionImpl";
- private static final boolean SSI_DBG = true;
-
- SipProfile mPeerProfile;
- SipSessionListenerProxy mProxy = new SipSessionListenerProxy();
- int mState = SipSession.State.READY_TO_CALL;
- RequestEvent mInviteReceived;
- Dialog mDialog;
- ServerTransaction mServerTransaction;
- ClientTransaction mClientTransaction;
- String mPeerSessionDescription;
- boolean mInCall;
- SessionTimer mSessionTimer;
- int mAuthenticationRetryCount;
-
- private SipKeepAlive mSipKeepAlive;
-
- private SipSessionImpl mSipSessionImpl;
-
- // the following three members are used for handling refer request.
- SipSessionImpl mReferSession;
- ReferredByHeader mReferredBy;
- String mReplaces;
-
- // lightweight timer
- class SessionTimer {
- private boolean mRunning = true;
-
- void start(final int timeout) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- sleep(timeout);
- if (mRunning) timeout();
- }
- }, "SipSessionTimerThread").start();
- }
-
- synchronized void cancel() {
- mRunning = false;
- this.notify();
- }
-
- private void timeout() {
- synchronized (SipSessionGroup.this) {
- onError(SipErrorCode.TIME_OUT, "Session timed out!");
- }
- }
-
- private synchronized void sleep(int timeout) {
- try {
- this.wait(timeout * 1000);
- } catch (InterruptedException e) {
- loge("session timer interrupted!", e);
- }
- }
- }
-
- public SipSessionImpl(ISipSessionListener listener) {
- setListener(listener);
- }
-
- SipSessionImpl duplicate() {
- return new SipSessionImpl(mProxy.getListener());
- }
-
- private void reset() {
- mInCall = false;
- removeSipSession(this);
- mPeerProfile = null;
- mState = SipSession.State.READY_TO_CALL;
- mInviteReceived = null;
- mPeerSessionDescription = null;
- mAuthenticationRetryCount = 0;
- mReferSession = null;
- mReferredBy = null;
- mReplaces = null;
-
- if (mDialog != null) mDialog.delete();
- mDialog = null;
-
- try {
- if (mServerTransaction != null) mServerTransaction.terminate();
- } catch (ObjectInUseException e) {
- // ignored
- }
- mServerTransaction = null;
-
- try {
- if (mClientTransaction != null) mClientTransaction.terminate();
- } catch (ObjectInUseException e) {
- // ignored
- }
- mClientTransaction = null;
-
- cancelSessionTimer();
-
- if (mSipSessionImpl != null) {
- mSipSessionImpl.stopKeepAliveProcess();
- mSipSessionImpl = null;
- }
- }
-
- @Override
- public boolean isInCall() {
- return mInCall;
- }
-
- @Override
- public String getLocalIp() {
- return mLocalIp;
- }
-
- @Override
- public SipProfile getLocalProfile() {
- return mLocalProfile;
- }
-
- @Override
- public SipProfile getPeerProfile() {
- return mPeerProfile;
- }
-
- @Override
- public String getCallId() {
- return SipHelper.getCallId(getTransaction());
- }
-
- private Transaction getTransaction() {
- if (mClientTransaction != null) return mClientTransaction;
- if (mServerTransaction != null) return mServerTransaction;
- return null;
- }
-
- @Override
- public int getState() {
- return mState;
- }
-
- @Override
- public void setListener(ISipSessionListener listener) {
- mProxy.setListener((listener instanceof SipSessionListenerProxy)
- ? ((SipSessionListenerProxy) listener).getListener()
- : listener);
- }
-
- // process the command in a new thread
- private void doCommandAsync(final EventObject command) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- processCommand(command);
- } catch (Throwable e) {
- loge("command error: " + command + ": "
- + mLocalProfile.getUriString(),
- getRootCause(e));
- onError(e);
- }
- }
- }, "SipSessionAsyncCmdThread").start();
- }
-
- @Override
- public void makeCall(SipProfile peerProfile, String sessionDescription,
- int timeout) {
- doCommandAsync(new MakeCallCommand(peerProfile, sessionDescription,
- timeout));
- }
-
- @Override
- public void answerCall(String sessionDescription, int timeout) {
- synchronized (SipSessionGroup.this) {
- if (mPeerProfile == null) return;
- doCommandAsync(new MakeCallCommand(mPeerProfile,
- sessionDescription, timeout));
- }
- }
-
- @Override
- public void endCall() {
- doCommandAsync(END_CALL);
- }
-
- @Override
- public void changeCall(String sessionDescription, int timeout) {
- synchronized (SipSessionGroup.this) {
- if (mPeerProfile == null) return;
- doCommandAsync(new MakeCallCommand(mPeerProfile,
- sessionDescription, timeout));
- }
- }
-
- @Override
- public void register(int duration) {
- doCommandAsync(new RegisterCommand(duration));
- }
-
- @Override
- public void unregister() {
- doCommandAsync(DEREGISTER);
- }
-
- private void processCommand(EventObject command) throws SipException {
- if (isLoggable(command)) log("process cmd: " + command);
- if (!process(command)) {
- onError(SipErrorCode.IN_PROGRESS,
- "cannot initiate a new transaction to execute: "
- + command);
- }
- }
-
- protected String generateTag() {
- // 32-bit randomness
- return String.valueOf((long) (Math.random() * 0x100000000L));
- }
-
- @Override
- public String toString() {
- try {
- String s = super.toString();
- return s.substring(s.indexOf("@")) + ":"
- + SipSession.State.toString(mState);
- } catch (Throwable e) {
- return super.toString();
- }
- }
-
- public boolean process(EventObject evt) throws SipException {
- if (isLoggable(this, evt)) log(" ~~~~~ " + this + ": "
- + SipSession.State.toString(mState) + ": processing "
- + logEvt(evt));
- synchronized (SipSessionGroup.this) {
- if (isClosed()) return false;
-
- if (mSipKeepAlive != null) {
- // event consumed by keepalive process
- if (mSipKeepAlive.process(evt)) return true;
- }
-
- Dialog dialog = null;
- if (evt instanceof RequestEvent) {
- dialog = ((RequestEvent) evt).getDialog();
- } else if (evt instanceof ResponseEvent) {
- dialog = ((ResponseEvent) evt).getDialog();
- extractExternalAddress((ResponseEvent) evt);
- }
- if (dialog != null) mDialog = dialog;
-
- boolean processed;
-
- switch (mState) {
- case SipSession.State.REGISTERING:
- case SipSession.State.DEREGISTERING:
- processed = registeringToReady(evt);
- break;
- case SipSession.State.READY_TO_CALL:
- processed = readyForCall(evt);
- break;
- case SipSession.State.INCOMING_CALL:
- processed = incomingCall(evt);
- break;
- case SipSession.State.INCOMING_CALL_ANSWERING:
- processed = incomingCallToInCall(evt);
- break;
- case SipSession.State.OUTGOING_CALL:
- case SipSession.State.OUTGOING_CALL_RING_BACK:
- processed = outgoingCall(evt);
- break;
- case SipSession.State.OUTGOING_CALL_CANCELING:
- processed = outgoingCallToReady(evt);
- break;
- case SipSession.State.IN_CALL:
- processed = inCall(evt);
- break;
- case SipSession.State.ENDING_CALL:
- processed = endingCall(evt);
- break;
- default:
- processed = false;
- }
- return (processed || processExceptions(evt));
- }
- }
-
- private boolean processExceptions(EventObject evt) throws SipException {
- if (isRequestEvent(Request.BYE, evt)) {
- // terminate the call whenever a BYE is received
- mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
- endCallNormally();
- return true;
- } else if (isRequestEvent(Request.CANCEL, evt)) {
- mSipHelper.sendResponse((RequestEvent) evt,
- Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST);
- return true;
- } else if (evt instanceof TransactionTerminatedEvent) {
- if (isCurrentTransaction((TransactionTerminatedEvent) evt)) {
- if (evt instanceof TimeoutEvent) {
- processTimeout((TimeoutEvent) evt);
- } else {
- processTransactionTerminated(
- (TransactionTerminatedEvent) evt);
- }
- return true;
- }
- } else if (isRequestEvent(Request.OPTIONS, evt)) {
- mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
- return true;
- } else if (evt instanceof DialogTerminatedEvent) {
- processDialogTerminated((DialogTerminatedEvent) evt);
- return true;
- }
- return false;
- }
-
- private void processDialogTerminated(DialogTerminatedEvent event) {
- if (mDialog == event.getDialog()) {
- onError(new SipException("dialog terminated"));
- } else {
- if (SSI_DBG) log("not the current dialog; current=" + mDialog
- + ", terminated=" + event.getDialog());
- }
- }
-
- private boolean isCurrentTransaction(TransactionTerminatedEvent event) {
- Transaction current = event.isServerTransaction()
- ? mServerTransaction
- : mClientTransaction;
- Transaction target = event.isServerTransaction()
- ? event.getServerTransaction()
- : event.getClientTransaction();
-
- if ((current != target) && (mState != SipSession.State.PINGING)) {
- if (SSI_DBG) log("not the current transaction; current="
- + toString(current) + ", target=" + toString(target));
- return false;
- } else if (current != null) {
- if (SSI_DBG) log("transaction terminated: " + toString(current));
- return true;
- } else {
- // no transaction; shouldn't be here; ignored
- return true;
- }
- }
-
- private String toString(Transaction transaction) {
- if (transaction == null) return "null";
- Request request = transaction.getRequest();
- Dialog dialog = transaction.getDialog();
- CSeqHeader cseq = (CSeqHeader) request.getHeader(CSeqHeader.NAME);
- return String.format("req=%s,%s,s=%s,ds=%s,", request.getMethod(),
- cseq.getSeqNumber(), transaction.getState(),
- ((dialog == null) ? "-" : dialog.getState()));
- }
-
- private void processTransactionTerminated(
- TransactionTerminatedEvent event) {
- switch (mState) {
- case SipSession.State.IN_CALL:
- case SipSession.State.READY_TO_CALL:
- if (SSI_DBG) log("Transaction terminated; do nothing");
- break;
- default:
- if (SSI_DBG) log("Transaction terminated early: " + this);
- onError(SipErrorCode.TRANSACTION_TERMINTED,
- "transaction terminated");
- }
- }
-
- private void processTimeout(TimeoutEvent event) {
- if (SSI_DBG) log("processing Timeout...");
- switch (mState) {
- case SipSession.State.REGISTERING:
- case SipSession.State.DEREGISTERING:
- reset();
- mProxy.onRegistrationTimeout(this);
- break;
- case SipSession.State.INCOMING_CALL:
- case SipSession.State.INCOMING_CALL_ANSWERING:
- case SipSession.State.OUTGOING_CALL:
- case SipSession.State.OUTGOING_CALL_CANCELING:
- onError(SipErrorCode.TIME_OUT, event.toString());
- break;
-
- default:
- if (SSI_DBG) log(" do nothing");
- break;
- }
- }
-
- private int getExpiryTime(Response response) {
- int time = -1;
- ContactHeader contact = (ContactHeader) response.getHeader(ContactHeader.NAME);
- if (contact != null) {
- time = contact.getExpires();
- }
- ExpiresHeader expires = (ExpiresHeader) response.getHeader(ExpiresHeader.NAME);
- if (expires != null && (time < 0 || time > expires.getExpires())) {
- time = expires.getExpires();
- }
- if (time <= 0) {
- time = EXPIRY_TIME;
- }
- expires = (ExpiresHeader) response.getHeader(MinExpiresHeader.NAME);
- if (expires != null && time < expires.getExpires()) {
- time = expires.getExpires();
- }
- if (SSI_DBG) {
- log("Expiry time = " + time);
- }
- return time;
- }
-
- private boolean registeringToReady(EventObject evt)
- throws SipException {
- if (expectResponse(Request.REGISTER, evt)) {
- ResponseEvent event = (ResponseEvent) evt;
- Response response = event.getResponse();
-
- int statusCode = response.getStatusCode();
- switch (statusCode) {
- case Response.OK:
- int state = mState;
- onRegistrationDone((state == SipSession.State.REGISTERING)
- ? getExpiryTime(((ResponseEvent) evt).getResponse())
- : -1);
- return true;
- case Response.UNAUTHORIZED:
- case Response.PROXY_AUTHENTICATION_REQUIRED:
- handleAuthentication(event);
- return true;
- default:
- if (statusCode >= 500) {
- onRegistrationFailed(response);
- return true;
- }
- }
- }
- return false;
- }
-
- private boolean handleAuthentication(ResponseEvent event)
- throws SipException {
- Response response = event.getResponse();
- String nonce = getNonceFromResponse(response);
- if (nonce == null) {
- onError(SipErrorCode.SERVER_ERROR,
- "server does not provide challenge");
- return false;
- } else if (mAuthenticationRetryCount < 2) {
- mClientTransaction = mSipHelper.handleChallenge(
- event, getAccountManager());
- mDialog = mClientTransaction.getDialog();
- mAuthenticationRetryCount++;
- if (isLoggable(this, event)) {
- if (SSI_DBG) log(" authentication retry count="
- + mAuthenticationRetryCount);
- }
- return true;
- } else {
- if (crossDomainAuthenticationRequired(response)) {
- onError(SipErrorCode.CROSS_DOMAIN_AUTHENTICATION,
- getRealmFromResponse(response));
- } else {
- onError(SipErrorCode.INVALID_CREDENTIALS,
- "incorrect username or password");
- }
- return false;
- }
- }
-
- private boolean crossDomainAuthenticationRequired(Response response) {
- String realm = getRealmFromResponse(response);
- if (realm == null) realm = "";
- return !mLocalProfile.getSipDomain().trim().equals(realm.trim());
- }
-
- private AccountManager getAccountManager() {
- return new AccountManager() {
- @Override
- public UserCredentials getCredentials(ClientTransaction
- challengedTransaction, String realm) {
- return new UserCredentials() {
- @Override
- public String getUserName() {
- String username = mLocalProfile.getAuthUserName();
- return (!TextUtils.isEmpty(username) ? username :
- mLocalProfile.getUserName());
- }
-
- @Override
- public String getPassword() {
- return mPassword;
- }
-
- @Override
- public String getSipDomain() {
- return mLocalProfile.getSipDomain();
- }
- };
- }
- };
- }
-
- private String getRealmFromResponse(Response response) {
- WWWAuthenticate wwwAuth = (WWWAuthenticate)response.getHeader(
- SIPHeaderNames.WWW_AUTHENTICATE);
- if (wwwAuth != null) return wwwAuth.getRealm();
- ProxyAuthenticate proxyAuth = (ProxyAuthenticate)response.getHeader(
- SIPHeaderNames.PROXY_AUTHENTICATE);
- return (proxyAuth == null) ? null : proxyAuth.getRealm();
- }
-
- private String getNonceFromResponse(Response response) {
- WWWAuthenticate wwwAuth = (WWWAuthenticate)response.getHeader(
- SIPHeaderNames.WWW_AUTHENTICATE);
- if (wwwAuth != null) return wwwAuth.getNonce();
- ProxyAuthenticate proxyAuth = (ProxyAuthenticate)response.getHeader(
- SIPHeaderNames.PROXY_AUTHENTICATE);
- return (proxyAuth == null) ? null : proxyAuth.getNonce();
- }
-
- private String getResponseString(int statusCode) {
- StatusLine statusLine = new StatusLine();
- statusLine.setStatusCode(statusCode);
- statusLine.setReasonPhrase(SIPResponse.getReasonPhrase(statusCode));
- return statusLine.encode();
- }
-
- 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();
- if (mReferSession != null) {
- mSipHelper.sendReferNotify(mReferSession.mDialog,
- getResponseString(Response.TRYING));
- }
- mClientTransaction = mSipHelper.sendInvite(
- mLocalProfile, mPeerProfile, cmd.getSessionDescription(),
- generateTag(), mReferredBy, mReplaces);
- mDialog = mClientTransaction.getDialog();
- addSipSession(this);
- startSessionTimer(cmd.getTimeout());
- mProxy.onCalling(this);
- 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);
- mProxy.onRegistering(this);
- return true;
- } else if (DEREGISTER == evt) {
- mState = SipSession.State.DEREGISTERING;
- mClientTransaction = mSipHelper.sendRegister(mLocalProfile,
- generateTag(), 0);
- mDialog = mClientTransaction.getDialog();
- addSipSession(this);
- mProxy.onRegistering(this);
- return true;
- }
- return false;
- }
-
- private boolean incomingCall(EventObject evt) throws SipException {
- // 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,
- mExternalIp, mExternalPort);
- startSessionTimer(((MakeCallCommand) evt).getTimeout());
- return true;
- } else if (END_CALL == evt) {
- mSipHelper.sendInviteBusyHere(mInviteReceived,
- mServerTransaction);
- endCallNormally();
- return true;
- } else if (isRequestEvent(Request.CANCEL, evt)) {
- RequestEvent event = (RequestEvent) evt;
- mSipHelper.sendResponse(event, Response.OK);
- mSipHelper.sendInviteRequestTerminated(
- mInviteReceived.getRequest(), mServerTransaction);
- endCallNormally();
- return true;
- }
- return false;
- }
-
- private boolean incomingCallToInCall(EventObject evt) {
- // expect ACK, CANCEL request
- if (isRequestEvent(Request.ACK, evt)) {
- String sdp = extractContent(((RequestEvent) evt).getRequest());
- if (sdp != null) mPeerSessionDescription = sdp;
- if (mPeerSessionDescription == null) {
- onError(SipErrorCode.CLIENT_ERROR, "peer sdp is empty");
- } else {
- establishCall(false);
- }
- return true;
- } else if (isRequestEvent(Request.CANCEL, evt)) {
- // http://tools.ietf.org/html/rfc3261#section-9.2
- // Final response has been sent; do nothing here.
- return true;
- }
- return false;
- }
-
- private boolean outgoingCall(EventObject evt) throws SipException {
- if (expectResponse(Request.INVITE, evt)) {
- ResponseEvent event = (ResponseEvent) evt;
- Response response = event.getResponse();
-
- int statusCode = response.getStatusCode();
- switch (statusCode) {
- case Response.RINGING:
- case Response.CALL_IS_BEING_FORWARDED:
- case Response.QUEUED:
- case Response.SESSION_PROGRESS:
- // feedback any provisional responses (except TRYING) as
- // ring back for better UX
- if (mState == SipSession.State.OUTGOING_CALL) {
- mState = SipSession.State.OUTGOING_CALL_RING_BACK;
- cancelSessionTimer();
- mProxy.onRingingBack(this);
- }
- return true;
- case Response.OK:
- if (mReferSession != null) {
- mSipHelper.sendReferNotify(mReferSession.mDialog,
- getResponseString(Response.OK));
- // since we don't need to remember the session anymore.
- mReferSession = null;
- }
- mSipHelper.sendInviteAck(event, mDialog);
- mPeerSessionDescription = extractContent(response);
- establishCall(true);
- return true;
- case Response.UNAUTHORIZED:
- case Response.PROXY_AUTHENTICATION_REQUIRED:
- if (handleAuthentication(event)) {
- addSipSession(this);
- }
- return true;
- case Response.REQUEST_PENDING:
- // TODO: rfc3261#section-14.1; re-schedule invite
- return true;
- default:
- if (mReferSession != null) {
- mSipHelper.sendReferNotify(mReferSession.mDialog,
- getResponseString(Response.SERVICE_UNAVAILABLE));
- }
- if (statusCode >= 400) {
- // error: an ack is sent automatically by the stack
- onError(response);
- return true;
- } else if (statusCode >= 300) {
- // TODO: handle 3xx (redirect)
- } else {
- return true;
- }
- }
- return false;
- } else if (END_CALL == evt) {
- // RFC says that UA should not send out cancel when no
- // response comes back yet. We are cheating for not checking
- // response.
- mState = SipSession.State.OUTGOING_CALL_CANCELING;
- mSipHelper.sendCancel(mClientTransaction);
- startSessionTimer(CANCEL_CALL_TIMER);
- return true;
- } else if (isRequestEvent(Request.INVITE, evt)) {
- // Call self? Send BUSY HERE so server may redirect the call to
- // voice mailbox.
- RequestEvent event = (RequestEvent) evt;
- mSipHelper.sendInviteBusyHere(event,
- event.getServerTransaction());
- return true;
- }
- return false;
- }
-
- private boolean outgoingCallToReady(EventObject evt)
- throws SipException {
- if (evt instanceof ResponseEvent) {
- ResponseEvent event = (ResponseEvent) evt;
- Response response = event.getResponse();
- int statusCode = response.getStatusCode();
- if (expectResponse(Request.CANCEL, evt)) {
- if (statusCode == Response.OK) {
- // do nothing; wait for REQUEST_TERMINATED
- return true;
- }
- } else if (expectResponse(Request.INVITE, evt)) {
- switch (statusCode) {
- case Response.OK:
- outgoingCall(evt); // abort Cancel
- return true;
- case Response.REQUEST_TERMINATED:
- endCallNormally();
- return true;
- }
- } else {
- return false;
- }
-
- if (statusCode >= 400) {
- onError(response);
- return true;
- }
- } else if (evt instanceof TransactionTerminatedEvent) {
- // rfc3261#section-14.1:
- // if re-invite gets timed out, terminate the dialog; but
- // re-invite is not reliable, just let it go and pretend
- // nothing happened.
- onError(new SipException("timed out"));
- }
- return false;
- }
-
- private boolean processReferRequest(RequestEvent event)
- throws SipException {
- try {
- ReferToHeader referto = (ReferToHeader) event.getRequest()
- .getHeader(ReferTo.NAME);
- Address address = referto.getAddress();
- SipURI uri = (SipURI) address.getURI();
- String replacesHeader = uri.getHeader(ReplacesHeader.NAME);
- String username = uri.getUser();
- if (username == null) {
- mSipHelper.sendResponse(event, Response.BAD_REQUEST);
- return false;
- }
- // send notify accepted
- mSipHelper.sendResponse(event, Response.ACCEPTED);
- SipSessionImpl newSession = createNewSession(event,
- this.mProxy.getListener(),
- mSipHelper.getServerTransaction(event),
- SipSession.State.READY_TO_CALL);
- newSession.mReferSession = this;
- newSession.mReferredBy = (ReferredByHeader) event.getRequest()
- .getHeader(ReferredByHeader.NAME);
- newSession.mReplaces = replacesHeader;
- newSession.mPeerProfile = createPeerProfile(referto);
- newSession.mProxy.onCallTransferring(newSession,
- null);
- return true;
- } catch (IllegalArgumentException e) {
- throw new SipException("createPeerProfile()", e);
- }
- }
-
- private boolean inCall(EventObject evt) throws SipException {
- // expect END_CALL cmd, BYE request, hold call (MakeCallCommand)
- // OK retransmission is handled in SipStack
- if (END_CALL == evt) {
- // rfc3261#section-15.1.1
- mState = SipSession.State.ENDING_CALL;
- mSipHelper.sendBye(mDialog);
- mProxy.onCallEnded(this);
- startSessionTimer(END_CALL_TIMER);
- return true;
- } else if (isRequestEvent(Request.INVITE, evt)) {
- // got Re-INVITE
- mState = SipSession.State.INCOMING_CALL;
- RequestEvent event = mInviteReceived = (RequestEvent) evt;
- mPeerSessionDescription = extractContent(event.getRequest());
- mServerTransaction = null;
- mProxy.onRinging(this, mPeerProfile, mPeerSessionDescription);
- return true;
- } else if (isRequestEvent(Request.BYE, evt)) {
- mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
- endCallNormally();
- return true;
- } else if (isRequestEvent(Request.REFER, evt)) {
- return processReferRequest((RequestEvent) evt);
- } else if (evt instanceof MakeCallCommand) {
- // to change call
- mState = SipSession.State.OUTGOING_CALL;
- mClientTransaction = mSipHelper.sendReinvite(mDialog,
- ((MakeCallCommand) evt).getSessionDescription());
- startSessionTimer(((MakeCallCommand) evt).getTimeout());
- return true;
- } else if (evt instanceof ResponseEvent) {
- if (expectResponse(Request.NOTIFY, evt)) return true;
- }
- return false;
- }
-
- private boolean endingCall(EventObject evt) throws SipException {
- if (expectResponse(Request.BYE, evt)) {
- ResponseEvent event = (ResponseEvent) evt;
- Response response = event.getResponse();
-
- int statusCode = response.getStatusCode();
- switch (statusCode) {
- case Response.UNAUTHORIZED:
- case Response.PROXY_AUTHENTICATION_REQUIRED:
- if (handleAuthentication(event)) {
- return true;
- } else {
- // can't authenticate; pass through to end session
- }
- }
- cancelSessionTimer();
- reset();
- return true;
- }
- return false;
- }
-
- // timeout in seconds
- private void startSessionTimer(int timeout) {
- if (timeout > 0) {
- mSessionTimer = new SessionTimer();
- mSessionTimer.start(timeout);
- }
- }
-
- private void cancelSessionTimer() {
- if (mSessionTimer != null) {
- mSessionTimer.cancel();
- mSessionTimer = null;
- }
- }
-
- private String createErrorMessage(Response response) {
- return String.format("%s (%d)", response.getReasonPhrase(),
- response.getStatusCode());
- }
-
- private void enableKeepAlive() {
- if (mSipSessionImpl != null) {
- mSipSessionImpl.stopKeepAliveProcess();
- } else {
- mSipSessionImpl = duplicate();
- }
- try {
- mSipSessionImpl.startKeepAliveProcess(
- INCALL_KEEPALIVE_INTERVAL, mPeerProfile, null);
- } catch (SipException e) {
- loge("keepalive cannot be enabled; ignored", e);
- mSipSessionImpl.stopKeepAliveProcess();
- }
- }
-
- private void establishCall(boolean enableKeepAlive) {
- mState = SipSession.State.IN_CALL;
- cancelSessionTimer();
- if (!mInCall && enableKeepAlive) enableKeepAlive();
- mInCall = true;
- mProxy.onCallEstablished(this, mPeerSessionDescription);
- }
-
- private void endCallNormally() {
- reset();
- mProxy.onCallEnded(this);
- }
-
- private void endCallOnError(int errorCode, String message) {
- reset();
- mProxy.onError(this, errorCode, message);
- }
-
- private void endCallOnBusy() {
- reset();
- mProxy.onCallBusy(this);
- }
-
- private void onError(int errorCode, String message) {
- cancelSessionTimer();
- switch (mState) {
- case SipSession.State.REGISTERING:
- case SipSession.State.DEREGISTERING:
- onRegistrationFailed(errorCode, message);
- break;
- default:
- endCallOnError(errorCode, message);
- }
- }
-
-
- private void onError(Throwable exception) {
- exception = getRootCause(exception);
- onError(getErrorCode(exception), exception.toString());
- }
-
- private void onError(Response response) {
- int statusCode = response.getStatusCode();
- if (!mInCall && (statusCode == Response.BUSY_HERE)) {
- endCallOnBusy();
- } else {
- onError(getErrorCode(statusCode), createErrorMessage(response));
- }
- }
-
- private int getErrorCode(int responseStatusCode) {
- switch (responseStatusCode) {
- case Response.TEMPORARILY_UNAVAILABLE:
- case Response.FORBIDDEN:
- case Response.GONE:
- case Response.NOT_FOUND:
- case Response.NOT_ACCEPTABLE:
- case Response.NOT_ACCEPTABLE_HERE:
- return SipErrorCode.PEER_NOT_REACHABLE;
-
- case Response.REQUEST_URI_TOO_LONG:
- case Response.ADDRESS_INCOMPLETE:
- case Response.AMBIGUOUS:
- return SipErrorCode.INVALID_REMOTE_URI;
-
- case Response.REQUEST_TIMEOUT:
- return SipErrorCode.TIME_OUT;
-
- default:
- if (responseStatusCode < 500) {
- return SipErrorCode.CLIENT_ERROR;
- } else {
- return SipErrorCode.SERVER_ERROR;
- }
- }
- }
-
- private int getErrorCode(Throwable exception) {
- String message = exception.getMessage();
- if (exception instanceof UnknownHostException) {
- return SipErrorCode.SERVER_UNREACHABLE;
- } else if (exception instanceof IOException) {
- return SipErrorCode.SOCKET_ERROR;
- } else {
- return SipErrorCode.CLIENT_ERROR;
- }
- }
-
- private void onRegistrationDone(int duration) {
- reset();
- mProxy.onRegistrationDone(this, duration);
- }
-
- private void onRegistrationFailed(int errorCode, String message) {
- reset();
- mProxy.onRegistrationFailed(this, errorCode, message);
- }
-
- private void onRegistrationFailed(Response response) {
- int statusCode = response.getStatusCode();
- onRegistrationFailed(getErrorCode(statusCode),
- createErrorMessage(response));
- }
-
- // Notes: SipSessionListener will be replaced by the keepalive process
- // @param interval in seconds
- public void startKeepAliveProcess(int interval,
- KeepAliveProcessCallback callback) throws SipException {
- synchronized (SipSessionGroup.this) {
- startKeepAliveProcess(interval, mLocalProfile, callback);
- }
- }
-
- // Notes: SipSessionListener will be replaced by the keepalive process
- // @param interval in seconds
- public void startKeepAliveProcess(int interval, SipProfile peerProfile,
- KeepAliveProcessCallback callback) throws SipException {
- synchronized (SipSessionGroup.this) {
- if (mSipKeepAlive != null) {
- throw new SipException("Cannot create more than one "
- + "keepalive process in a SipSession");
- }
- mPeerProfile = peerProfile;
- mSipKeepAlive = new SipKeepAlive();
- mProxy.setListener(mSipKeepAlive);
- mSipKeepAlive.start(interval, callback);
- }
- }
-
- public void stopKeepAliveProcess() {
- synchronized (SipSessionGroup.this) {
- if (mSipKeepAlive != null) {
- mSipKeepAlive.stop();
- mSipKeepAlive = null;
- }
- }
- }
-
- class SipKeepAlive extends SipSessionAdapter implements Runnable {
- private static final String SKA_TAG = "SipKeepAlive";
- private static final boolean SKA_DBG = true;
-
- private boolean mRunning = false;
- private KeepAliveProcessCallback mCallback;
-
- private boolean mPortChanged = false;
- private int mRPort = 0;
- private int mInterval; // just for debugging
-
- // @param interval in seconds
- void start(int interval, KeepAliveProcessCallback callback) {
- if (mRunning) return;
- mRunning = true;
- mInterval = interval;
- mCallback = new KeepAliveProcessCallbackProxy(callback);
- mWakeupTimer.set(interval * 1000, this);
- if (SKA_DBG) {
- log("start keepalive:"
- + mLocalProfile.getUriString());
- }
-
- // No need to run the first time in a separate thread for now
- run();
- }
-
- // return true if the event is consumed
- boolean process(EventObject evt) {
- if (mRunning && (mState == SipSession.State.PINGING)) {
- if (evt instanceof ResponseEvent) {
- if (parseOptionsResult(evt)) {
- if (mPortChanged) {
- resetExternalAddress();
- stop();
- } else {
- cancelSessionTimer();
- removeSipSession(SipSessionImpl.this);
- }
- mCallback.onResponse(mPortChanged);
- return true;
- }
- }
- }
- return false;
- }
-
- // SipSessionAdapter
- // To react to the session timeout event and network error.
- @Override
- public void onError(ISipSession session, int errorCode, String message) {
- stop();
- mCallback.onError(errorCode, message);
- }
-
- // SipWakeupTimer timeout handler
- // To send out keepalive message.
- @Override
- public void run() {
- synchronized (SipSessionGroup.this) {
- if (!mRunning) return;
-
- if (DBG_PING) {
- String peerUri = (mPeerProfile == null)
- ? "null"
- : mPeerProfile.getUriString();
- log("keepalive: " + mLocalProfile.getUriString()
- + " --> " + peerUri + ", interval=" + mInterval);
- }
- try {
- sendKeepAlive();
- } catch (Throwable t) {
- if (SKA_DBG) {
- loge("keepalive error: "
- + mLocalProfile.getUriString(), getRootCause(t));
- }
- // It's possible that the keepalive process is being stopped
- // during session.sendKeepAlive() so need to check mRunning
- // again here.
- if (mRunning) SipSessionImpl.this.onError(t);
- }
- }
- }
-
- void stop() {
- synchronized (SipSessionGroup.this) {
- if (SKA_DBG) {
- log("stop keepalive:" + mLocalProfile.getUriString()
- + ",RPort=" + mRPort);
- }
- mRunning = false;
- mWakeupTimer.cancel(this);
- reset();
- }
- }
-
- private void sendKeepAlive() throws SipException {
- synchronized (SipSessionGroup.this) {
- mState = SipSession.State.PINGING;
- mClientTransaction = mSipHelper.sendOptions(
- mLocalProfile, mPeerProfile, generateTag());
- mDialog = mClientTransaction.getDialog();
- addSipSession(SipSessionImpl.this);
-
- startSessionTimer(KEEPALIVE_TIMEOUT);
- // when timed out, onError() will be called with SipErrorCode.TIME_OUT
- }
- }
-
- private boolean parseOptionsResult(EventObject evt) {
- if (expectResponse(Request.OPTIONS, evt)) {
- ResponseEvent event = (ResponseEvent) evt;
- int rPort = getRPortFromResponse(event.getResponse());
- if (rPort != -1) {
- if (mRPort == 0) mRPort = rPort;
- if (mRPort != rPort) {
- mPortChanged = true;
- if (SKA_DBG) log(String.format(
- "rport is changed: %d <> %d", mRPort, rPort));
- mRPort = rPort;
- } else {
- if (SKA_DBG) log("rport is the same: " + rPort);
- }
- } else {
- if (SKA_DBG) log("peer did not respond rport");
- }
- return true;
- }
- return false;
- }
-
- private int getRPortFromResponse(Response response) {
- ViaHeader viaHeader = (ViaHeader)(response.getHeader(
- SIPHeaderNames.VIA));
- return (viaHeader == null) ? -1 : viaHeader.getRPort();
- }
-
- private void log(String s) {
- Rlog.d(SKA_TAG, s);
- }
- }
-
- private void log(String s) {
- Rlog.d(SSI_TAG, s);
- }
- }
-
- /**
- * @return true if the event is a request event matching the specified
- * method; false otherwise
- */
- private static boolean isRequestEvent(String method, EventObject event) {
- try {
- if (event instanceof RequestEvent) {
- RequestEvent requestEvent = (RequestEvent) event;
- return method.equals(requestEvent.getRequest().getMethod());
- }
- } catch (Throwable e) {
- }
- return false;
- }
-
- private static String getCseqMethod(Message message) {
- return ((CSeqHeader) message.getHeader(CSeqHeader.NAME)).getMethod();
- }
-
- /**
- * @return true if the event is a response event and the CSeqHeader method
- * match the given arguments; false otherwise
- */
- private static boolean expectResponse(
- String expectedMethod, EventObject evt) {
- if (evt instanceof ResponseEvent) {
- ResponseEvent event = (ResponseEvent) evt;
- Response response = event.getResponse();
- return expectedMethod.equalsIgnoreCase(getCseqMethod(response));
- }
- return false;
- }
-
- private static SipProfile createPeerProfile(HeaderAddress header)
- throws SipException {
- try {
- Address address = header.getAddress();
- SipURI uri = (SipURI) address.getURI();
- String username = uri.getUser();
- if (username == null) username = ANONYMOUS;
- int port = uri.getPort();
- SipProfile.Builder builder =
- new SipProfile.Builder(username, uri.getHost())
- .setDisplayName(address.getDisplayName());
- if (port > 0) builder.setPort(port);
- return builder.build();
- } catch (IllegalArgumentException e) {
- throw new SipException("createPeerProfile()", e);
- } catch (ParseException e) {
- throw new SipException("createPeerProfile()", e);
- }
- }
-
- private static boolean isLoggable(SipSessionImpl s) {
- if (s != null) {
- switch (s.mState) {
- case SipSession.State.PINGING:
- return DBG_PING;
- }
- }
- return DBG;
- }
-
- private static boolean isLoggable(EventObject evt) {
- return isLoggable(null, evt);
- }
-
- private static boolean isLoggable(SipSessionImpl s, EventObject evt) {
- if (!isLoggable(s)) return false;
- if (evt == null) return false;
-
- if (evt instanceof ResponseEvent) {
- Response response = ((ResponseEvent) evt).getResponse();
- if (Request.OPTIONS.equals(response.getHeader(CSeqHeader.NAME))) {
- return DBG_PING;
- }
- return DBG;
- } else if (evt instanceof RequestEvent) {
- if (isRequestEvent(Request.OPTIONS, evt)) {
- return DBG_PING;
- }
- return DBG;
- }
- return false;
- }
-
- private static String logEvt(EventObject evt) {
- if (evt instanceof RequestEvent) {
- return ((RequestEvent) evt).getRequest().toString();
- } else if (evt instanceof ResponseEvent) {
- return ((ResponseEvent) evt).getResponse().toString();
- } else {
- return evt.toString();
- }
- }
-
- private class RegisterCommand extends EventObject {
- private int mDuration;
-
- public RegisterCommand(int duration) {
- super(SipSessionGroup.this);
- mDuration = duration;
- }
-
- public int getDuration() {
- return mDuration;
- }
- }
-
- private class MakeCallCommand extends EventObject {
- private String mSessionDescription;
- private int mTimeout; // in seconds
-
- public MakeCallCommand(SipProfile peerProfile,
- String sessionDescription, int timeout) {
- super(peerProfile);
- mSessionDescription = sessionDescription;
- mTimeout = timeout;
- }
-
- public SipProfile getPeerProfile() {
- return (SipProfile) getSource();
- }
-
- public String getSessionDescription() {
- return mSessionDescription;
- }
-
- public int getTimeout() {
- return mTimeout;
- }
- }
-
- /** Class to help safely run KeepAliveProcessCallback in a different thread. */
- static class KeepAliveProcessCallbackProxy implements KeepAliveProcessCallback {
- private static final String KAPCP_TAG = "KeepAliveProcessCallbackProxy";
- private KeepAliveProcessCallback mCallback;
-
- KeepAliveProcessCallbackProxy(KeepAliveProcessCallback callback) {
- mCallback = callback;
- }
-
- private void proxy(Runnable runnable) {
- // One thread for each calling back.
- // Note: Guarantee ordering if the issue becomes important. Currently,
- // the chance of handling two callback events at a time is none.
- new Thread(runnable, "SIP-KeepAliveProcessCallbackThread").start();
- }
-
- @Override
- public void onResponse(final boolean portChanged) {
- if (mCallback == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mCallback.onResponse(portChanged);
- } catch (Throwable t) {
- loge("onResponse", t);
- }
- }
- });
- }
-
- @Override
- public void onError(final int errorCode, final String description) {
- if (mCallback == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mCallback.onError(errorCode, description);
- } catch (Throwable t) {
- loge("onError", t);
- }
- }
- });
- }
-
- private void loge(String s, Throwable t) {
- Rlog.e(KAPCP_TAG, s, t);
- }
- }
-
- private void log(String s) {
- Rlog.d(TAG, s);
- }
-
- private void loge(String s, Throwable t) {
- Rlog.e(TAG, s, t);
- }
-}
diff --git a/voip/java/com/android/server/sip/SipSessionListenerProxy.java b/voip/java/com/android/server/sip/SipSessionListenerProxy.java
deleted file mode 100644
index 7a4ae8d..0000000
--- a/voip/java/com/android/server/sip/SipSessionListenerProxy.java
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.sip;
-
-import android.net.sip.ISipSession;
-import android.net.sip.ISipSessionListener;
-import android.net.sip.SipProfile;
-import android.os.DeadObjectException;
-import android.telephony.Rlog;
-
-/** Class to help safely run a callback in a different thread. */
-class SipSessionListenerProxy extends ISipSessionListener.Stub {
- private static final String TAG = "SipSessionListnerProxy";
-
- private ISipSessionListener mListener;
-
- public void setListener(ISipSessionListener listener) {
- mListener = listener;
- }
-
- public ISipSessionListener getListener() {
- return mListener;
- }
-
- private void proxy(Runnable runnable) {
- // One thread for each calling back.
- // Note: Guarantee ordering if the issue becomes important. Currently,
- // the chance of handling two callback events at a time is none.
- new Thread(runnable, "SipSessionCallbackThread").start();
- }
-
- @Override
- public void onCalling(final ISipSession session) {
- if (mListener == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mListener.onCalling(session);
- } catch (Throwable t) {
- handle(t, "onCalling()");
- }
- }
- });
- }
-
- @Override
- public void onRinging(final ISipSession session, final SipProfile caller,
- final String sessionDescription) {
- if (mListener == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mListener.onRinging(session, caller, sessionDescription);
- } catch (Throwable t) {
- handle(t, "onRinging()");
- }
- }
- });
- }
-
- @Override
- public void onRingingBack(final ISipSession session) {
- if (mListener == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mListener.onRingingBack(session);
- } catch (Throwable t) {
- handle(t, "onRingingBack()");
- }
- }
- });
- }
-
- @Override
- public void onCallEstablished(final ISipSession session,
- final String sessionDescription) {
- if (mListener == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mListener.onCallEstablished(session, sessionDescription);
- } catch (Throwable t) {
- handle(t, "onCallEstablished()");
- }
- }
- });
- }
-
- @Override
- public void onCallEnded(final ISipSession session) {
- if (mListener == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mListener.onCallEnded(session);
- } catch (Throwable t) {
- handle(t, "onCallEnded()");
- }
- }
- });
- }
-
- @Override
- public void onCallTransferring(final ISipSession newSession,
- final String sessionDescription) {
- if (mListener == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mListener.onCallTransferring(newSession, sessionDescription);
- } catch (Throwable t) {
- handle(t, "onCallTransferring()");
- }
- }
- });
- }
-
- @Override
- public void onCallBusy(final ISipSession session) {
- if (mListener == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mListener.onCallBusy(session);
- } catch (Throwable t) {
- handle(t, "onCallBusy()");
- }
- }
- });
- }
-
- @Override
- public void onCallChangeFailed(final ISipSession session,
- final int errorCode, final String message) {
- if (mListener == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mListener.onCallChangeFailed(session, errorCode, message);
- } catch (Throwable t) {
- handle(t, "onCallChangeFailed()");
- }
- }
- });
- }
-
- @Override
- public void onError(final ISipSession session, final int errorCode,
- final String message) {
- if (mListener == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mListener.onError(session, errorCode, message);
- } catch (Throwable t) {
- handle(t, "onError()");
- }
- }
- });
- }
-
- @Override
- public void onRegistering(final ISipSession session) {
- if (mListener == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mListener.onRegistering(session);
- } catch (Throwable t) {
- handle(t, "onRegistering()");
- }
- }
- });
- }
-
- @Override
- public void onRegistrationDone(final ISipSession session,
- final int duration) {
- if (mListener == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mListener.onRegistrationDone(session, duration);
- } catch (Throwable t) {
- handle(t, "onRegistrationDone()");
- }
- }
- });
- }
-
- @Override
- public void onRegistrationFailed(final ISipSession session,
- final int errorCode, final String message) {
- if (mListener == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mListener.onRegistrationFailed(session, errorCode, message);
- } catch (Throwable t) {
- handle(t, "onRegistrationFailed()");
- }
- }
- });
- }
-
- @Override
- public void onRegistrationTimeout(final ISipSession session) {
- if (mListener == null) return;
- proxy(new Runnable() {
- @Override
- public void run() {
- try {
- mListener.onRegistrationTimeout(session);
- } catch (Throwable t) {
- handle(t, "onRegistrationTimeout()");
- }
- }
- });
- }
-
- private void handle(Throwable t, String message) {
- if (t instanceof DeadObjectException) {
- mListener = null;
- // This creates race but it's harmless. Just don't log the error
- // when it happens.
- } else if (mListener != null) {
- loge(message, t);
- }
- }
-
- private void log(String s) {
- Rlog.d(TAG, s);
- }
-
- private void loge(String s, Throwable t) {
- Rlog.e(TAG, s, t);
- }
-}
diff --git a/voip/java/com/android/server/sip/SipWakeLock.java b/voip/java/com/android/server/sip/SipWakeLock.java
deleted file mode 100644
index b3fbb56..0000000
--- a/voip/java/com/android/server/sip/SipWakeLock.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2010, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.sip;
-
-import android.os.PowerManager;
-import android.telephony.Rlog;
-
-import java.util.HashSet;
-
-class SipWakeLock {
- private static final String TAG = "SipWakeLock";
- private static final boolean DBG = false;
- private PowerManager mPowerManager;
- private PowerManager.WakeLock mWakeLock;
- private PowerManager.WakeLock mTimerWakeLock;
- private HashSet<Object> mHolders = new HashSet<Object>();
-
- SipWakeLock(PowerManager powerManager) {
- mPowerManager = powerManager;
- }
-
- synchronized void reset() {
- if (DBG) log("reset count=" + mHolders.size());
- mHolders.clear();
- release(null);
- }
-
- synchronized void acquire(long timeout) {
- if (mTimerWakeLock == null) {
- mTimerWakeLock = mPowerManager.newWakeLock(
- PowerManager.PARTIAL_WAKE_LOCK, "SipWakeLock.timer");
- mTimerWakeLock.setReferenceCounted(true);
- }
- mTimerWakeLock.acquire(timeout);
- }
-
- synchronized void acquire(Object holder) {
- mHolders.add(holder);
- if (mWakeLock == null) {
- mWakeLock = mPowerManager.newWakeLock(
- PowerManager.PARTIAL_WAKE_LOCK, "SipWakeLock");
- }
- if (!mWakeLock.isHeld()) mWakeLock.acquire();
- if (DBG) log("acquire count=" + mHolders.size());
- }
-
- synchronized void release(Object holder) {
- mHolders.remove(holder);
- if ((mWakeLock != null) && mHolders.isEmpty()
- && mWakeLock.isHeld()) {
- mWakeLock.release();
- }
- if (DBG) log("release count=" + mHolders.size());
- }
-
- private void log(String s) {
- Rlog.d(TAG, s);
- }
-}
diff --git a/voip/java/com/android/server/sip/SipWakeupTimer.java b/voip/java/com/android/server/sip/SipWakeupTimer.java
deleted file mode 100644
index 3ba43312..0000000
--- a/voip/java/com/android/server/sip/SipWakeupTimer.java
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
- * Copyright (C) 2011, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.sip;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.SystemClock;
-import android.telephony.Rlog;
-
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.TreeSet;
-import java.util.concurrent.Executor;
-
-/**
- * Timer that can schedule events to occur even when the device is in sleep.
- */
-class SipWakeupTimer extends BroadcastReceiver {
- private static final String TAG = "SipWakeupTimer";
- private static final boolean DBG = SipService.DBG && true; // STOPSHIP if true
- private static final String TRIGGER_TIME = "TriggerTime";
-
- private Context mContext;
- private AlarmManager mAlarmManager;
-
- // runnable --> time to execute in SystemClock
- private TreeSet<MyEvent> mEventQueue =
- new TreeSet<MyEvent>(new MyEventComparator());
-
- private PendingIntent mPendingIntent;
-
- private Executor mExecutor;
-
- public SipWakeupTimer(Context context, Executor executor) {
- mContext = context;
- mAlarmManager = (AlarmManager)
- context.getSystemService(Context.ALARM_SERVICE);
-
- IntentFilter filter = new IntentFilter(getAction());
- context.registerReceiver(this, filter);
- mExecutor = executor;
- }
-
- /**
- * Stops the timer. No event can be scheduled after this method is called.
- */
- public synchronized void stop() {
- mContext.unregisterReceiver(this);
- if (mPendingIntent != null) {
- mAlarmManager.cancel(mPendingIntent);
- mPendingIntent = null;
- }
- mEventQueue.clear();
- mEventQueue = null;
- }
-
- private boolean stopped() {
- if (mEventQueue == null) {
- if (DBG) log("Timer stopped");
- return true;
- } else {
- return false;
- }
- }
-
- private void cancelAlarm() {
- mAlarmManager.cancel(mPendingIntent);
- mPendingIntent = null;
- }
-
- private void recalculatePeriods() {
- if (mEventQueue.isEmpty()) return;
-
- MyEvent firstEvent = mEventQueue.first();
- int minPeriod = firstEvent.mMaxPeriod;
- long minTriggerTime = firstEvent.mTriggerTime;
- for (MyEvent e : mEventQueue) {
- e.mPeriod = e.mMaxPeriod / minPeriod * minPeriod;
- int interval = (int) (e.mLastTriggerTime + e.mMaxPeriod
- - minTriggerTime);
- interval = interval / minPeriod * minPeriod;
- e.mTriggerTime = minTriggerTime + interval;
- }
- TreeSet<MyEvent> newQueue = new TreeSet<MyEvent>(
- mEventQueue.comparator());
- newQueue.addAll(mEventQueue);
- mEventQueue.clear();
- mEventQueue = newQueue;
- if (DBG) {
- log("queue re-calculated");
- printQueue();
- }
- }
-
- // Determines the period and the trigger time of the new event and insert it
- // to the queue.
- private void insertEvent(MyEvent event) {
- long now = SystemClock.elapsedRealtime();
- if (mEventQueue.isEmpty()) {
- event.mTriggerTime = now + event.mPeriod;
- mEventQueue.add(event);
- return;
- }
- MyEvent firstEvent = mEventQueue.first();
- int minPeriod = firstEvent.mPeriod;
- if (minPeriod <= event.mMaxPeriod) {
- event.mPeriod = event.mMaxPeriod / minPeriod * minPeriod;
- int interval = event.mMaxPeriod;
- interval -= (int) (firstEvent.mTriggerTime - now);
- interval = interval / minPeriod * minPeriod;
- event.mTriggerTime = firstEvent.mTriggerTime + interval;
- mEventQueue.add(event);
- } else {
- long triggerTime = now + event.mPeriod;
- if (firstEvent.mTriggerTime < triggerTime) {
- event.mTriggerTime = firstEvent.mTriggerTime;
- event.mLastTriggerTime -= event.mPeriod;
- } else {
- event.mTriggerTime = triggerTime;
- }
- mEventQueue.add(event);
- recalculatePeriods();
- }
- }
-
- /**
- * Sets a periodic timer.
- *
- * @param period the timer period; in milli-second
- * @param callback is called back when the timer goes off; the same callback
- * can be specified in multiple timer events
- */
- public synchronized void set(int period, Runnable callback) {
- if (stopped()) return;
-
- long now = SystemClock.elapsedRealtime();
- MyEvent event = new MyEvent(period, callback, now);
- insertEvent(event);
-
- if (mEventQueue.first() == event) {
- if (mEventQueue.size() > 1) cancelAlarm();
- scheduleNext();
- }
-
- long triggerTime = event.mTriggerTime;
- if (DBG) {
- log("set: add event " + event + " scheduled on "
- + showTime(triggerTime) + " at " + showTime(now)
- + ", #events=" + mEventQueue.size());
- printQueue();
- }
- }
-
- /**
- * Cancels all the timer events with the specified callback.
- *
- * @param callback the callback
- */
- public synchronized void cancel(Runnable callback) {
- if (stopped() || mEventQueue.isEmpty()) return;
- if (DBG) log("cancel:" + callback);
-
- MyEvent firstEvent = mEventQueue.first();
- for (Iterator<MyEvent> iter = mEventQueue.iterator();
- iter.hasNext();) {
- MyEvent event = iter.next();
- if (event.mCallback == callback) {
- iter.remove();
- if (DBG) log(" cancel found:" + event);
- }
- }
- if (mEventQueue.isEmpty()) {
- cancelAlarm();
- } else if (mEventQueue.first() != firstEvent) {
- cancelAlarm();
- firstEvent = mEventQueue.first();
- firstEvent.mPeriod = firstEvent.mMaxPeriod;
- firstEvent.mTriggerTime = firstEvent.mLastTriggerTime
- + firstEvent.mPeriod;
- recalculatePeriods();
- scheduleNext();
- }
- if (DBG) {
- log("cancel: X");
- printQueue();
- }
- }
-
- private void scheduleNext() {
- if (stopped() || mEventQueue.isEmpty()) return;
-
- if (mPendingIntent != null) {
- throw new RuntimeException("pendingIntent is not null!");
- }
-
- MyEvent event = mEventQueue.first();
- Intent intent = new Intent(getAction());
- intent.putExtra(TRIGGER_TIME, event.mTriggerTime);
- PendingIntent pendingIntent = mPendingIntent =
- PendingIntent.getBroadcast(mContext, 0, intent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- event.mTriggerTime, pendingIntent);
- }
-
- @Override
- public synchronized void onReceive(Context context, Intent intent) {
- // This callback is already protected by AlarmManager's wake lock.
- String action = intent.getAction();
- if (getAction().equals(action)
- && intent.getExtras().containsKey(TRIGGER_TIME)) {
- mPendingIntent = null;
- long triggerTime = intent.getLongExtra(TRIGGER_TIME, -1L);
- execute(triggerTime);
- } else {
- log("onReceive: unrecognized intent: " + intent);
- }
- }
-
- private void printQueue() {
- int count = 0;
- for (MyEvent event : mEventQueue) {
- log(" " + event + ": scheduled at "
- + showTime(event.mTriggerTime) + ": last at "
- + showTime(event.mLastTriggerTime));
- if (++count >= 5) break;
- }
- if (mEventQueue.size() > count) {
- log(" .....");
- } else if (count == 0) {
- log(" <empty>");
- }
- }
-
- private void execute(long triggerTime) {
- if (DBG) log("time's up, triggerTime = "
- + showTime(triggerTime) + ": " + mEventQueue.size());
- if (stopped() || mEventQueue.isEmpty()) return;
-
- for (MyEvent event : mEventQueue) {
- if (event.mTriggerTime != triggerTime) continue;
- if (DBG) log("execute " + event);
-
- event.mLastTriggerTime = triggerTime;
- event.mTriggerTime += event.mPeriod;
-
- // run the callback in the handler thread to prevent deadlock
- mExecutor.execute(event.mCallback);
- }
- if (DBG) {
- log("after timeout execution");
- printQueue();
- }
- scheduleNext();
- }
-
- private String getAction() {
- return toString();
- }
-
- private String showTime(long time) {
- int ms = (int) (time % 1000);
- int s = (int) (time / 1000);
- int m = s / 60;
- s %= 60;
- return String.format("%d.%d.%d", m, s, ms);
- }
-
- private static class MyEvent {
- int mPeriod;
- int mMaxPeriod;
- long mTriggerTime;
- long mLastTriggerTime;
- Runnable mCallback;
-
- MyEvent(int period, Runnable callback, long now) {
- mPeriod = mMaxPeriod = period;
- mCallback = callback;
- mLastTriggerTime = now;
- }
-
- @Override
- public String toString() {
- String s = super.toString();
- s = s.substring(s.indexOf("@"));
- return s + ":" + (mPeriod / 1000) + ":" + (mMaxPeriod / 1000) + ":"
- + toString(mCallback);
- }
-
- private String toString(Object o) {
- String s = o.toString();
- int index = s.indexOf("$");
- if (index > 0) s = s.substring(index + 1);
- return s;
- }
- }
-
- // Sort the events by mMaxPeriod so that the first event can be used to
- // align events with larger periods
- private static class MyEventComparator implements Comparator<MyEvent> {
- @Override
- public int compare(MyEvent e1, MyEvent e2) {
- if (e1 == e2) return 0;
- int diff = e1.mMaxPeriod - e2.mMaxPeriod;
- if (diff == 0) diff = -1;
- return diff;
- }
-
- @Override
- public boolean equals(Object that) {
- return (this == that);
- }
- }
-
- private void log(String s) {
- Rlog.d(TAG, s);
- }
-}
diff --git a/voip/jni/rtp/AmrCodec.cpp b/voip/jni/rtp/AmrCodec.cpp
deleted file mode 100644
index e2d820e..0000000
--- a/voip/jni/rtp/AmrCodec.cpp
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyrightm (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <string.h>
-
-#include "AudioCodec.h"
-
-#include "gsmamr_dec.h"
-#include "gsmamr_enc.h"
-
-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, int count, 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.
- const 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, int count, 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:
- GsmEfrCodec() {
- if (AMREncodeInit(&mEncoder, &mSidSync, false)) {
- mEncoder = NULL;
- }
- if (GSMInitDecode(&mDecoder, (Word8 *)"RTP")) {
- mDecoder = NULL;
- }
- }
-
- ~GsmEfrCodec() {
- if (mEncoder) {
- AMREncodeExit(&mEncoder, &mSidSync);
- }
- if (mDecoder) {
- GSMDecodeFrameExit(&mDecoder);
- }
- }
-
- int set(int sampleRate, const char *fmtp) {
- return (sampleRate == 8000 && mEncoder && mDecoder) ? 160 : -1;
- }
-
- int encode(void *payload, int16_t *samples);
- int decode(int16_t *samples, int count, void *payload, int length);
-
-private:
- void *mEncoder;
- void *mSidSync;
- void *mDecoder;
-};
-
-int GsmEfrCodec::encode(void *payload, int16_t *samples)
-{
- unsigned char *bytes = (unsigned char *)payload;
- Frame_Type_3GPP type;
-
- int length = AMREncode(mEncoder, mSidSync, MR122,
- samples, bytes, &type, AMR_TX_WMF);
-
- if (type == AMR_122 && length == 32) {
- bytes[0] = 0xC0 | (bytes[1] >> 4);
- for (int i = 1; i < 31; ++i) {
- bytes[i] = (bytes[i] << 4) | (bytes[i + 1] >> 4);
- }
- return 31;
- }
- return -1;
-}
-
-int GsmEfrCodec::decode(int16_t *samples, int count, void *payload, int length)
-{
- unsigned char *bytes = (unsigned char *)payload;
- int n = 0;
- while (n + 160 <= count && length >= 31 && (bytes[0] >> 4) == 0x0C) {
- for (int i = 0; i < 30; ++i) {
- bytes[i] = (bytes[i] << 4) | (bytes[i + 1] >> 4);
- }
- bytes[30] <<= 4;
-
- if (AMRDecode(mDecoder, AMR_122, bytes, &samples[n], MIME_IETF) != 31) {
- break;
- }
- n += 160;
- length -= 31;
- bytes += 31;
- }
- return n;
-}
-
-} // namespace
-
-AudioCodec *newAmrCodec()
-{
- return new AmrCodec;
-}
-
-AudioCodec *newGsmEfrCodec()
-{
- return new GsmEfrCodec;
-}
diff --git a/voip/jni/rtp/Android.mk b/voip/jni/rtp/Android.mk
deleted file mode 100644
index b265cdd..0000000
--- a/voip/jni/rtp/Android.mk
+++ /dev/null
@@ -1,59 +0,0 @@
-#
-# Copyright (C) 2010 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := librtp_jni
-
-LOCAL_SRC_FILES := \
- AudioCodec.cpp \
- AudioGroup.cpp \
- EchoSuppressor.cpp \
- RtpStream.cpp \
- util.cpp \
- rtp_jni.cpp
-
-LOCAL_SRC_FILES += \
- AmrCodec.cpp \
- G711Codec.cpp \
- GsmCodec.cpp
-
-LOCAL_SHARED_LIBRARIES := \
- libnativehelper \
- libcutils \
- libutils \
- libmedia \
- libstagefright_amrnb_common
-
-LOCAL_STATIC_LIBRARIES := libgsm libstagefright_amrnbdec libstagefright_amrnbenc
-
-LOCAL_C_INCLUDES += \
- $(JNI_H_INCLUDE) \
- external/libgsm/inc \
- frameworks/av/media/libstagefright/codecs/amrnb/common/include \
- frameworks/av/media/libstagefright/codecs/amrnb/common/ \
- frameworks/av/media/libstagefright/codecs/amrnb/enc/include \
- frameworks/av/media/libstagefright/codecs/amrnb/enc/src \
- frameworks/av/media/libstagefright/codecs/amrnb/dec/include \
- frameworks/av/media/libstagefright/codecs/amrnb/dec/src \
- $(call include-path-for, audio-effects)
-
-LOCAL_CFLAGS += -fvisibility=hidden
-
-
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/voip/jni/rtp/AudioCodec.cpp b/voip/jni/rtp/AudioCodec.cpp
deleted file mode 100644
index c75fbc9..0000000
--- a/voip/jni/rtp/AudioCodec.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyrightm (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <strings.h>
-
-#include "AudioCodec.h"
-
-extern AudioCodec *newAlawCodec();
-extern AudioCodec *newUlawCodec();
-extern AudioCodec *newGsmCodec();
-extern AudioCodec *newAmrCodec();
-extern AudioCodec *newGsmEfrCodec();
-
-struct AudioCodecType {
- const char *name;
- AudioCodec *(*create)();
-} gAudioCodecTypes[] = {
- {"PCMA", newAlawCodec},
- {"PCMU", newUlawCodec},
- {"GSM", newGsmCodec},
- {"AMR", newAmrCodec},
- {"GSM-EFR", newGsmEfrCodec},
- {NULL, NULL},
-};
-
-AudioCodec *newAudioCodec(const char *codecName)
-{
- AudioCodecType *type = gAudioCodecTypes;
- while (type->name != NULL) {
- if (strcasecmp(codecName, type->name) == 0) {
- AudioCodec *codec = type->create();
- codec->name = type->name;
- return codec;
- }
- ++type;
- }
- return NULL;
-}
diff --git a/voip/jni/rtp/AudioCodec.h b/voip/jni/rtp/AudioCodec.h
deleted file mode 100644
index 741730b..0000000
--- a/voip/jni/rtp/AudioCodec.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyrightm (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdint.h>
-
-#ifndef __AUDIO_CODEC_H__
-#define __AUDIO_CODEC_H__
-
-class AudioCodec
-{
-public:
- const char *name;
- // Needed by destruction through base class pointers.
- virtual ~AudioCodec() {}
- // Returns sampleCount or non-positive value if unsupported.
- virtual int set(int sampleRate, const char *fmtp) = 0;
- // Returns the length of payload in bytes.
- virtual int encode(void *payload, int16_t *samples) = 0;
- // Returns the number of decoded samples.
- virtual int decode(int16_t *samples, int count, void *payload, int length) = 0;
-};
-
-AudioCodec *newAudioCodec(const char *codecName);
-
-#endif
diff --git a/voip/jni/rtp/AudioGroup.cpp b/voip/jni/rtp/AudioGroup.cpp
deleted file mode 100644
index 2f0829e..0000000
--- a/voip/jni/rtp/AudioGroup.cpp
+++ /dev/null
@@ -1,1073 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/epoll.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <time.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-
-// #define LOG_NDEBUG 0
-#define LOG_TAG "AudioGroup"
-#include <cutils/atomic.h>
-#include <cutils/properties.h>
-#include <utils/Log.h>
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/threads.h>
-#include <utils/SystemClock.h>
-#include <media/AudioSystem.h>
-#include <media/AudioRecord.h>
-#include <media/AudioTrack.h>
-#include <media/mediarecorder.h>
-#include <media/AudioEffect.h>
-#include <audio_effects/effect_aec.h>
-#include <system/audio.h>
-
-#include "jni.h"
-#include "JNIHelp.h"
-
-#include "AudioCodec.h"
-#include "EchoSuppressor.h"
-
-extern int parse(JNIEnv *env, jstring jAddress, int port, sockaddr_storage *ss);
-
-namespace {
-
-using namespace android;
-
-int gRandom = -1;
-
-// We use a circular array to implement jitter buffer. The simplest way is doing
-// 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 2048ms long and assume packet interval is 50ms or less.
-// The first 100ms is the place where samples get mixed. The rest is the real
-// jitter buffer. For a stream at 8000Hz it takes 32 kilobytes. These numbers
-// are chosen by experiments and each of them can be adjusted as needed.
-
-// Originally a stream does not send packets when it is receive-only or there is
-// nothing to mix. However, this causes some problems with certain firewalls and
-// proxies. A firewall might remove a port mapping when there is no outgoing
-// packet for a preiod of time, and a proxy might wait for incoming packets from
-// both sides before start forwarding. To solve these problems, we send out a
-// silence packet on the stream for every second. It should be good enough to
-// keep the stream alive with relatively low resources.
-
-// Other notes:
-// + We use elapsedRealtime() to get the time. Since we use 32bit variables
-// instead of 64bit ones, comparison must be done by subtraction.
-// + Sampling rate must be multiple of 1000Hz, and packet length must be in
-// 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 only 8000Hz is supported.
-
-#define BUFFER_SIZE 2048
-#define HISTORY_SIZE 100
-#define MEASURE_BASE 100
-#define MEASURE_PERIOD 5000
-#define DTMF_PERIOD 200
-
-class AudioStream
-{
-public:
- AudioStream();
- ~AudioStream();
- bool set(int mode, int socket, sockaddr_storage *remote,
- AudioCodec *codec, int sampleRate, int sampleCount,
- int codecType, int dtmfType);
-
- void sendDtmf(int event);
- bool mix(int32_t *output, int head, int tail, int sampleRate);
- void encode(int tick, AudioStream *chain);
- void decode(int tick);
-
-private:
- enum {
- NORMAL = 0,
- SEND_ONLY = 1,
- RECEIVE_ONLY = 2,
- LAST_MODE = 2,
- };
-
- int mMode;
- int mSocket;
- sockaddr_storage mRemote;
- AudioCodec *mCodec;
- uint32_t mCodecMagic;
- uint32_t mDtmfMagic;
- bool mFixRemote;
-
- int mTick;
- int mSampleRate;
- int mSampleCount;
- int mInterval;
- int mKeepAlive;
-
- int16_t *mBuffer;
- int mBufferMask;
- int mBufferHead;
- int mBufferTail;
- int mLatencyTimer;
- int mLatencyScore;
-
- uint16_t mSequence;
- uint32_t mTimestamp;
- uint32_t mSsrc;
-
- int mDtmfEvent;
- int mDtmfStart;
-
- AudioStream *mNext;
-
- friend class AudioGroup;
-};
-
-AudioStream::AudioStream()
-{
- mSocket = -1;
- mCodec = NULL;
- mBuffer = NULL;
- mNext = NULL;
-}
-
-AudioStream::~AudioStream()
-{
- close(mSocket);
- delete mCodec;
- delete [] mBuffer;
- ALOGD("stream[%d] is dead", mSocket);
-}
-
-bool AudioStream::set(int mode, int socket, sockaddr_storage *remote,
- AudioCodec *codec, int sampleRate, int sampleCount,
- int codecType, int dtmfType)
-{
- if (mode < 0 || mode > LAST_MODE) {
- return false;
- }
- mMode = mode;
-
- mCodecMagic = (0x8000 | codecType) << 16;
- mDtmfMagic = (dtmfType == -1) ? 0 : (0x8000 | dtmfType) << 16;
-
- mTick = elapsedRealtime();
- mSampleRate = sampleRate / 1000;
- mSampleCount = sampleCount;
- mInterval = mSampleCount / mSampleRate;
-
- // Allocate jitter buffer.
- 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.
- read(gRandom, &mSequence, sizeof(mSequence));
- read(gRandom, &mTimestamp, sizeof(mTimestamp));
- read(gRandom, &mSsrc, sizeof(mSsrc));
-
- mDtmfEvent = -1;
- mDtmfStart = 0;
-
- // Only take over these things when succeeded.
- mSocket = socket;
- if (codec) {
- mRemote = *remote;
- mCodec = codec;
-
- // Here we should never get an private address, but some buggy proxy
- // servers do give us one. To solve this, we replace the address when
- // the first time we successfully decode an incoming packet.
- mFixRemote = false;
- if (remote->ss_family == AF_INET) {
- unsigned char *address =
- (unsigned char *)&((sockaddr_in *)remote)->sin_addr;
- if (address[0] == 10 ||
- (address[0] == 172 && (address[1] >> 4) == 1) ||
- (address[0] == 192 && address[1] == 168)) {
- mFixRemote = true;
- }
- }
- }
-
- ALOGD("stream[%d] is configured as %s %dkHz %dms mode %d", mSocket,
- (codec ? codec->name : "RAW"), mSampleRate, mInterval, mMode);
- return true;
-}
-
-void AudioStream::sendDtmf(int event)
-{
- if (mDtmfMagic != 0) {
- mDtmfEvent = event << 24;
- mDtmfStart = mTimestamp + mSampleCount;
- }
-}
-
-bool AudioStream::mix(int32_t *output, int head, int tail, int sampleRate)
-{
- if (mMode == SEND_ONLY) {
- return false;
- }
-
- if (head - mBufferHead < 0) {
- head = mBufferHead;
- }
- if (tail - mBufferTail > 0) {
- tail = mBufferTail;
- }
- if (tail - head <= 0) {
- return false;
- }
-
- head *= mSampleRate;
- tail *= mSampleRate;
-
- if (sampleRate == mSampleRate) {
- for (int i = head; i - tail < 0; ++i) {
- output[i - head] += mBuffer[i & mBufferMask];
- }
- } else {
- // TODO: implement resampling.
- return false;
- }
- return true;
-}
-
-void AudioStream::encode(int tick, AudioStream *chain)
-{
- if (tick - mTick >= mInterval) {
- // We just missed the train. Pretend that packets in between are lost.
- int skipped = (tick - mTick) / mInterval;
- mTick += skipped * mInterval;
- mSequence += skipped;
- mTimestamp += skipped * mSampleCount;
- ALOGV("stream[%d] skips %d packets", mSocket, skipped);
- }
-
- tick = mTick;
- mTick += mInterval;
- ++mSequence;
- mTimestamp += mSampleCount;
-
- // If there is an ongoing DTMF event, send it now.
- if (mMode != RECEIVE_ONLY && mDtmfEvent != -1) {
- int duration = mTimestamp - mDtmfStart;
- // Make sure duration is reasonable.
- if (duration >= 0 && duration < mSampleRate * DTMF_PERIOD) {
- duration += mSampleCount;
- int32_t buffer[4] = {
- htonl(mDtmfMagic | mSequence),
- htonl(mDtmfStart),
- mSsrc,
- htonl(mDtmfEvent | duration),
- };
- if (duration >= mSampleRate * DTMF_PERIOD) {
- buffer[3] |= htonl(1 << 23);
- mDtmfEvent = -1;
- }
- sendto(mSocket, buffer, sizeof(buffer), MSG_DONTWAIT,
- (sockaddr *)&mRemote, sizeof(mRemote));
- return;
- }
- mDtmfEvent = -1;
- }
-
- int32_t buffer[mSampleCount + 3];
- bool data = false;
- if (mMode != RECEIVE_ONLY) {
- // Mix all other streams.
- memset(buffer, 0, sizeof(buffer));
- while (chain) {
- if (chain != this) {
- data |= chain->mix(buffer, tick - mInterval, tick, mSampleRate);
- }
- chain = chain->mNext;
- }
- }
-
- int16_t samples[mSampleCount];
- if (data) {
- // Saturate into 16 bits.
- for (int i = 0; i < mSampleCount; ++i) {
- int32_t sample = buffer[i];
- if (sample < -32768) {
- sample = -32768;
- }
- if (sample > 32767) {
- sample = 32767;
- }
- samples[i] = sample;
- }
- } else {
- if ((mTick ^ mKeepAlive) >> 10 == 0) {
- return;
- }
- mKeepAlive = mTick;
- memset(samples, 0, sizeof(samples));
-
- if (mMode != RECEIVE_ONLY) {
- ALOGV("stream[%d] no data", mSocket);
- }
- }
-
- if (!mCodec) {
- // Special case for device stream.
- send(mSocket, samples, sizeof(samples), MSG_DONTWAIT);
- return;
- }
-
- // Cook the packet and send it out.
- buffer[0] = htonl(mCodecMagic | mSequence);
- buffer[1] = htonl(mTimestamp);
- buffer[2] = mSsrc;
- int length = mCodec->encode(&buffer[3], samples);
- if (length <= 0) {
- ALOGV("stream[%d] encoder error", mSocket);
- return;
- }
- sendto(mSocket, buffer, length + 12, MSG_DONTWAIT, (sockaddr *)&mRemote,
- sizeof(mRemote));
-}
-
-void AudioStream::decode(int tick)
-{
- char c;
- if (mMode == SEND_ONLY) {
- recv(mSocket, &c, 1, MSG_DONTWAIT);
- return;
- }
-
- // Make sure mBufferHead and mBufferTail are reasonable.
- if ((unsigned int)(tick + BUFFER_SIZE - mBufferHead) > BUFFER_SIZE * 2) {
- mBufferHead = tick - HISTORY_SIZE;
- mBufferTail = mBufferHead;
- }
-
- if (tick - mBufferHead > HISTORY_SIZE) {
- // Throw away outdated samples.
- mBufferHead = tick - HISTORY_SIZE;
- if (mBufferTail - mBufferHead < 0) {
- mBufferTail = mBufferHead;
- }
- }
-
- // Adjust the jitter buffer if the latency keeps larger than the threshold
- // in the measurement period.
- int score = mBufferTail - tick - MEASURE_BASE;
- if (mLatencyScore > score || mLatencyScore <= 0) {
- mLatencyScore = score;
- mLatencyTimer = tick;
- } else if (tick - mLatencyTimer >= MEASURE_PERIOD) {
- ALOGV("stream[%d] reduces latency of %dms", mSocket, mLatencyScore);
- mBufferTail -= mLatencyScore;
- mLatencyScore = -1;
- }
-
- int count = (BUFFER_SIZE - (mBufferTail - mBufferHead)) * mSampleRate;
- if (count < mSampleCount) {
- // Buffer overflow. Drop the packet.
- ALOGV("stream[%d] buffer overflow", mSocket);
- recv(mSocket, &c, 1, MSG_DONTWAIT);
- return;
- }
-
- // Receive the packet and decode it.
- int16_t samples[count];
- if (!mCodec) {
- // Special case for device stream.
- count = recv(mSocket, samples, sizeof(samples),
- MSG_TRUNC | MSG_DONTWAIT) >> 1;
- } else {
- __attribute__((aligned(4))) uint8_t buffer[2048];
- sockaddr_storage remote;
- socklen_t addrlen = sizeof(remote);
-
- int length = recvfrom(mSocket, buffer, sizeof(buffer),
- MSG_TRUNC | MSG_DONTWAIT, (sockaddr *)&remote, &addrlen);
-
- // Do we need to check SSRC, sequence, and timestamp? They are not
- // reliable but at least they can be used to identify duplicates?
- if (length < 12 || length > (int)sizeof(buffer) ||
- (ntohl(*(uint32_t *)buffer) & 0xC07F0000) != mCodecMagic) {
- ALOGV("stream[%d] malformed packet", mSocket);
- return;
- }
- int offset = 12 + ((buffer[0] & 0x0F) << 2);
- if ((buffer[0] & 0x10) != 0) {
- offset += 4 + (ntohs(*(uint16_t *)&buffer[offset + 2]) << 2);
- }
- if ((buffer[0] & 0x20) != 0) {
- length -= buffer[length - 1];
- }
- length -= offset;
- if (length >= 0) {
- length = mCodec->decode(samples, count, &buffer[offset], length);
- }
- if (length > 0 && mFixRemote) {
- mRemote = remote;
- mFixRemote = false;
- }
- count = length;
- }
- if (count <= 0) {
- ALOGV("stream[%d] decoder error", mSocket);
- return;
- }
-
- if (tick - mBufferTail > 0) {
- // Buffer underrun. Reset the jitter buffer.
- ALOGV("stream[%d] buffer underrun", mSocket);
- if (mBufferTail - mBufferHead <= 0) {
- mBufferHead = tick + mInterval;
- mBufferTail = mBufferHead;
- } else {
- int tail = (tick + mInterval) * mSampleRate;
- for (int i = mBufferTail * mSampleRate; i - tail < 0; ++i) {
- mBuffer[i & mBufferMask] = 0;
- }
- mBufferTail = tick + mInterval;
- }
- }
-
- // Append to the jitter buffer.
- int tail = mBufferTail * mSampleRate;
- for (int i = 0; i < count; ++i) {
- mBuffer[tail & mBufferMask] = samples[i];
- ++tail;
- }
- mBufferTail += mInterval;
-}
-
-//------------------------------------------------------------------------------
-
-class AudioGroup
-{
-public:
- AudioGroup();
- ~AudioGroup();
- bool set(int sampleRate, int sampleCount);
-
- bool setMode(int mode);
- bool sendDtmf(int event);
- bool add(AudioStream *stream);
- bool remove(AudioStream *stream);
- bool platformHasAec() { return mPlatformHasAec; }
-
-private:
- enum {
- ON_HOLD = 0,
- MUTED = 1,
- NORMAL = 2,
- ECHO_SUPPRESSION = 3,
- LAST_MODE = 3,
- };
-
- bool checkPlatformAec();
-
- AudioStream *mChain;
- int mEventQueue;
- volatile int mDtmfEvent;
-
- int mMode;
- int mSampleRate;
- int mSampleCount;
- int mDeviceSocket;
- bool mPlatformHasAec;
-
- class NetworkThread : public Thread
- {
- public:
- NetworkThread(AudioGroup *group) : Thread(false), mGroup(group) {}
-
- bool start()
- {
- if (run("Network", ANDROID_PRIORITY_AUDIO) != NO_ERROR) {
- ALOGE("cannot start network thread");
- return false;
- }
- return true;
- }
-
- private:
- AudioGroup *mGroup;
- bool threadLoop();
- };
- sp<NetworkThread> mNetworkThread;
-
- class DeviceThread : public Thread
- {
- public:
- DeviceThread(AudioGroup *group) : Thread(false), mGroup(group) {}
-
- bool start()
- {
- if (run("Device", ANDROID_PRIORITY_AUDIO) != NO_ERROR) {
- ALOGE("cannot start device thread");
- return false;
- }
- return true;
- }
-
- private:
- AudioGroup *mGroup;
- bool threadLoop();
- };
- sp<DeviceThread> mDeviceThread;
-};
-
-AudioGroup::AudioGroup()
-{
- mMode = ON_HOLD;
- mChain = NULL;
- mEventQueue = -1;
- mDtmfEvent = -1;
- mDeviceSocket = -1;
- mNetworkThread = new NetworkThread(this);
- mDeviceThread = new DeviceThread(this);
- mPlatformHasAec = checkPlatformAec();
-}
-
-AudioGroup::~AudioGroup()
-{
- mNetworkThread->requestExitAndWait();
- mDeviceThread->requestExitAndWait();
- close(mEventQueue);
- close(mDeviceSocket);
- while (mChain) {
- AudioStream *next = mChain->mNext;
- delete mChain;
- mChain = next;
- }
- ALOGD("group[%d] is dead", mDeviceSocket);
-}
-
-bool AudioGroup::set(int sampleRate, int sampleCount)
-{
- mEventQueue = epoll_create(2);
- if (mEventQueue == -1) {
- ALOGE("epoll_create: %s", strerror(errno));
- return false;
- }
-
- mSampleRate = sampleRate;
- mSampleCount = sampleCount;
-
- // Create device socket.
- int pair[2];
- if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair)) {
- ALOGE("socketpair: %s", strerror(errno));
- return false;
- }
- mDeviceSocket = pair[0];
-
- // Create device stream.
- mChain = new AudioStream;
- if (!mChain->set(AudioStream::NORMAL, pair[1], NULL, NULL,
- sampleRate, sampleCount, -1, -1)) {
- close(pair[1]);
- ALOGE("cannot initialize device stream");
- return false;
- }
-
- // Give device socket a reasonable timeout.
- timeval tv;
- tv.tv_sec = 0;
- tv.tv_usec = 1000 * sampleCount / sampleRate * 500;
- if (setsockopt(pair[0], SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) {
- ALOGE("setsockopt: %s", strerror(errno));
- return false;
- }
-
- // Add device stream into event queue.
- epoll_event event;
- event.events = EPOLLIN;
- event.data.ptr = mChain;
- if (epoll_ctl(mEventQueue, EPOLL_CTL_ADD, pair[1], &event)) {
- ALOGE("epoll_ctl: %s", strerror(errno));
- return false;
- }
-
- // Anything else?
- ALOGD("stream[%d] joins group[%d]", pair[1], pair[0]);
- return true;
-}
-
-bool AudioGroup::setMode(int mode)
-{
- if (mode < 0 || mode > LAST_MODE) {
- return false;
- }
- // FIXME: temporary code to overcome echo and mic gain issues on herring and tuna boards.
- // Must be modified/removed when the root cause of the issue is fixed in the hardware or
- // driver
- char value[PROPERTY_VALUE_MAX];
- property_get("ro.product.board", value, "");
- if (mode == NORMAL &&
- (!strcmp(value, "herring") || !strcmp(value, "tuna"))) {
- mode = ECHO_SUPPRESSION;
- }
- if (mMode == mode) {
- return true;
- }
-
- mDeviceThread->requestExitAndWait();
- ALOGD("group[%d] switches from mode %d to %d", mDeviceSocket, mMode, mode);
- mMode = mode;
- return (mode == ON_HOLD) || mDeviceThread->start();
-}
-
-bool AudioGroup::sendDtmf(int event)
-{
- if (event < 0 || event > 15) {
- return false;
- }
-
- // DTMF is rarely used, so we try to make it as lightweight as possible.
- // Using volatile might be dodgy, but using a pipe or pthread primitives
- // or stop-set-restart threads seems too heavy. Will investigate later.
- timespec ts;
- ts.tv_sec = 0;
- ts.tv_nsec = 100000000;
- for (int i = 0; mDtmfEvent != -1 && i < 20; ++i) {
- nanosleep(&ts, NULL);
- }
- if (mDtmfEvent != -1) {
- return false;
- }
- mDtmfEvent = event;
- nanosleep(&ts, NULL);
- return true;
-}
-
-bool AudioGroup::add(AudioStream *stream)
-{
- mNetworkThread->requestExitAndWait();
-
- epoll_event event;
- event.events = EPOLLIN;
- event.data.ptr = stream;
- if (epoll_ctl(mEventQueue, EPOLL_CTL_ADD, stream->mSocket, &event)) {
- ALOGE("epoll_ctl: %s", strerror(errno));
- return false;
- }
-
- stream->mNext = mChain->mNext;
- mChain->mNext = stream;
- if (!mNetworkThread->start()) {
- // Only take over the stream when succeeded.
- mChain->mNext = stream->mNext;
- return false;
- }
-
- ALOGD("stream[%d] joins group[%d]", stream->mSocket, mDeviceSocket);
- return true;
-}
-
-bool AudioGroup::remove(AudioStream *stream)
-{
- mNetworkThread->requestExitAndWait();
-
- for (AudioStream *chain = mChain; chain->mNext; chain = chain->mNext) {
- if (chain->mNext == stream) {
- if (epoll_ctl(mEventQueue, EPOLL_CTL_DEL, stream->mSocket, NULL)) {
- ALOGE("epoll_ctl: %s", strerror(errno));
- return false;
- }
- chain->mNext = stream->mNext;
- ALOGD("stream[%d] leaves group[%d]", stream->mSocket, mDeviceSocket);
- delete stream;
- break;
- }
- }
-
- // Do not start network thread if there is only one stream.
- if (!mChain->mNext || !mNetworkThread->start()) {
- return false;
- }
- return true;
-}
-
-bool AudioGroup::NetworkThread::threadLoop()
-{
- AudioStream *chain = mGroup->mChain;
- int tick = elapsedRealtime();
- int deadline = tick + 10;
- int count = 0;
-
- for (AudioStream *stream = chain; stream; stream = stream->mNext) {
- if (tick - stream->mTick >= 0) {
- stream->encode(tick, chain);
- }
- if (deadline - stream->mTick > 0) {
- deadline = stream->mTick;
- }
- ++count;
- }
-
- int event = mGroup->mDtmfEvent;
- if (event != -1) {
- for (AudioStream *stream = chain; stream; stream = stream->mNext) {
- stream->sendDtmf(event);
- }
- mGroup->mDtmfEvent = -1;
- }
-
- deadline -= tick;
- if (deadline < 1) {
- deadline = 1;
- }
-
- epoll_event events[count];
- count = epoll_wait(mGroup->mEventQueue, events, count, deadline);
- if (count == -1) {
- ALOGE("epoll_wait: %s", strerror(errno));
- return false;
- }
- for (int i = 0; i < count; ++i) {
- ((AudioStream *)events[i].data.ptr)->decode(tick);
- }
-
- return true;
-}
-
-bool AudioGroup::checkPlatformAec()
-{
- effect_descriptor_t fxDesc;
- uint32_t numFx;
-
- if (AudioEffect::queryNumberEffects(&numFx) != NO_ERROR) {
- return false;
- }
- for (uint32_t i = 0; i < numFx; i++) {
- if (AudioEffect::queryEffect(i, &fxDesc) != NO_ERROR) {
- continue;
- }
- if (memcmp(&fxDesc.type, FX_IID_AEC, sizeof(effect_uuid_t)) == 0) {
- return true;
- }
- }
- return false;
-}
-
-bool AudioGroup::DeviceThread::threadLoop()
-{
- int mode = mGroup->mMode;
- int sampleRate = mGroup->mSampleRate;
- int sampleCount = mGroup->mSampleCount;
- int deviceSocket = mGroup->mDeviceSocket;
-
- // Find out the frame count for AudioTrack and AudioRecord.
- size_t output = 0;
- size_t input = 0;
- if (AudioTrack::getMinFrameCount(&output, AUDIO_STREAM_VOICE_CALL,
- sampleRate) != NO_ERROR || output <= 0 ||
- AudioRecord::getMinFrameCount(&input, sampleRate,
- AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_MONO) != NO_ERROR || input <= 0) {
- ALOGE("cannot compute frame count");
- return false;
- }
- ALOGD("reported frame count: output %d, input %d", output, input);
-
- if (output < sampleCount * 2) {
- output = sampleCount * 2;
- }
- if (input < sampleCount * 2) {
- input = sampleCount * 2;
- }
- ALOGD("adjusted frame count: output %d, input %d", output, input);
-
- // Initialize AudioTrack and AudioRecord.
- AudioTrack track;
- AudioRecord record;
- if (track.set(AUDIO_STREAM_VOICE_CALL, sampleRate, AUDIO_FORMAT_PCM_16_BIT,
- AUDIO_CHANNEL_OUT_MONO, output) != NO_ERROR ||
- record.set(AUDIO_SOURCE_VOICE_COMMUNICATION, sampleRate, AUDIO_FORMAT_PCM_16_BIT,
- AUDIO_CHANNEL_IN_MONO, input) != NO_ERROR) {
- ALOGE("cannot initialize audio device");
- return false;
- }
- ALOGD("latency: output %d, input %d", track.latency(), record.latency());
-
- // Give device socket a reasonable buffer size.
- setsockopt(deviceSocket, SOL_SOCKET, SO_RCVBUF, &output, sizeof(output));
- setsockopt(deviceSocket, SOL_SOCKET, SO_SNDBUF, &output, sizeof(output));
-
- // Drain device socket.
- char c;
- while (recv(deviceSocket, &c, 1, MSG_DONTWAIT) == 1);
-
- // check if platform supports echo cancellation and do not active local echo suppression in
- // this case
- EchoSuppressor *echo = NULL;
- AudioEffect *aec = NULL;
- if (mode == ECHO_SUPPRESSION) {
- if (mGroup->platformHasAec()) {
- aec = new AudioEffect(FX_IID_AEC,
- NULL,
- 0,
- 0,
- 0,
- record.getSessionId(),
- record.getInput());
- status_t status = aec->initCheck();
- if (status == NO_ERROR || status == ALREADY_EXISTS) {
- aec->setEnabled(true);
- } else {
- delete aec;
- aec = NULL;
- }
- }
- // Create local echo suppressor if platform AEC cannot be used.
- if (aec == NULL) {
- echo = new EchoSuppressor(sampleCount,
- (track.latency() + record.latency()) * sampleRate / 1000);
- }
- }
- // Start AudioRecord before AudioTrack. This prevents AudioTrack from being
- // disabled due to buffer underrun while waiting for AudioRecord.
- if (mode != MUTED) {
- record.start();
- int16_t one;
- record.read(&one, sizeof(one));
- }
- track.start();
-
- while (!exitPending()) {
- int16_t output[sampleCount];
- if (recv(deviceSocket, output, sizeof(output), 0) <= 0) {
- memset(output, 0, sizeof(output));
- }
-
- int16_t input[sampleCount];
- int toWrite = sampleCount;
- int toRead = (mode == MUTED) ? 0 : sampleCount;
- int chances = 100;
-
- while (--chances > 0 && (toWrite > 0 || toRead > 0)) {
- if (toWrite > 0) {
- AudioTrack::Buffer buffer;
- buffer.frameCount = toWrite;
-
- status_t status = track.obtainBuffer(&buffer, 1);
- if (status == NO_ERROR) {
- int offset = sampleCount - toWrite;
- memcpy(buffer.i8, &output[offset], buffer.size);
- toWrite -= buffer.frameCount;
- track.releaseBuffer(&buffer);
- } else if (status != TIMED_OUT && status != WOULD_BLOCK) {
- ALOGE("cannot write to AudioTrack");
- goto exit;
- }
- }
-
- if (toRead > 0) {
- AudioRecord::Buffer buffer;
- buffer.frameCount = toRead;
-
- status_t status = record.obtainBuffer(&buffer, 1);
- if (status == NO_ERROR) {
- int offset = sampleCount - toRead;
- memcpy(&input[offset], buffer.i8, buffer.size);
- toRead -= buffer.frameCount;
- record.releaseBuffer(&buffer);
- } else if (status != TIMED_OUT && status != WOULD_BLOCK) {
- ALOGE("cannot read from AudioRecord");
- goto exit;
- }
- }
- }
-
- if (chances <= 0) {
- ALOGW("device loop timeout");
- while (recv(deviceSocket, &c, 1, MSG_DONTWAIT) == 1);
- }
-
- if (mode != MUTED) {
- if (echo != NULL) {
- ALOGV("echo->run()");
- echo->run(output, input);
- }
- send(deviceSocket, input, sizeof(input), MSG_DONTWAIT);
- }
- }
-
-exit:
- delete echo;
- delete aec;
- return true;
-}
-
-//------------------------------------------------------------------------------
-
-static jfieldID gNative;
-static jfieldID gMode;
-
-int add(JNIEnv *env, jobject thiz, jint mode,
- jint socket, jstring jRemoteAddress, jint remotePort,
- jstring jCodecSpec, jint dtmfType)
-{
- AudioCodec *codec = NULL;
- AudioStream *stream = NULL;
- AudioGroup *group = NULL;
-
- // Sanity check.
- sockaddr_storage remote;
- if (parse(env, jRemoteAddress, remotePort, &remote) < 0) {
- // Exception already thrown.
- return 0;
- }
- if (!jCodecSpec) {
- jniThrowNullPointerException(env, "codecSpec");
- return 0;
- }
- const char *codecSpec = env->GetStringUTFChars(jCodecSpec, NULL);
- if (!codecSpec) {
- // Exception already thrown.
- return 0;
- }
- socket = dup(socket);
- if (socket == -1) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "cannot get stream socket");
- return 0;
- }
-
- // Create audio codec.
- int codecType = -1;
- char codecName[16];
- int sampleRate = -1;
- sscanf(codecSpec, "%d %15[^/]%*c%d", &codecType, codecName, &sampleRate);
- codec = newAudioCodec(codecName);
- int sampleCount = (codec ? codec->set(sampleRate, codecSpec) : -1);
- env->ReleaseStringUTFChars(jCodecSpec, codecSpec);
- if (sampleCount <= 0) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "cannot initialize audio codec");
- goto error;
- }
-
- // Create audio stream.
- stream = new AudioStream;
- if (!stream->set(mode, socket, &remote, codec, sampleRate, sampleCount,
- codecType, dtmfType)) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "cannot initialize audio stream");
- goto error;
- }
- socket = -1;
- codec = NULL;
-
- // Create audio group.
- group = (AudioGroup *)env->GetIntField(thiz, gNative);
- if (!group) {
- int mode = env->GetIntField(thiz, gMode);
- group = new AudioGroup;
- if (!group->set(8000, 256) || !group->setMode(mode)) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "cannot initialize audio group");
- goto error;
- }
- }
-
- // Add audio stream into audio group.
- if (!group->add(stream)) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "cannot add audio stream");
- goto error;
- }
-
- // Succeed.
- env->SetIntField(thiz, gNative, (int)group);
- return (int)stream;
-
-error:
- delete group;
- delete stream;
- delete codec;
- close(socket);
- env->SetIntField(thiz, gNative, 0);
- return 0;
-}
-
-void remove(JNIEnv *env, jobject thiz, jint stream)
-{
- AudioGroup *group = (AudioGroup *)env->GetIntField(thiz, gNative);
- if (group) {
- if (!stream || !group->remove((AudioStream *)stream)) {
- delete group;
- env->SetIntField(thiz, gNative, 0);
- }
- }
-}
-
-void setMode(JNIEnv *env, jobject thiz, jint mode)
-{
- AudioGroup *group = (AudioGroup *)env->GetIntField(thiz, gNative);
- if (group && !group->setMode(mode)) {
- jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- }
-}
-
-void sendDtmf(JNIEnv *env, jobject thiz, jint event)
-{
- AudioGroup *group = (AudioGroup *)env->GetIntField(thiz, gNative);
- if (group && !group->sendDtmf(event)) {
- jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- }
-}
-
-JNINativeMethod gMethods[] = {
- {"nativeAdd", "(IILjava/lang/String;ILjava/lang/String;I)I", (void *)add},
- {"nativeRemove", "(I)V", (void *)remove},
- {"nativeSetMode", "(I)V", (void *)setMode},
- {"nativeSendDtmf", "(I)V", (void *)sendDtmf},
-};
-
-} // namespace
-
-int registerAudioGroup(JNIEnv *env)
-{
- gRandom = open("/dev/urandom", O_RDONLY);
- if (gRandom == -1) {
- ALOGE("urandom: %s", strerror(errno));
- return -1;
- }
-
- jclass clazz;
- if ((clazz = env->FindClass("android/net/rtp/AudioGroup")) == NULL ||
- (gNative = env->GetFieldID(clazz, "mNative", "I")) == NULL ||
- (gMode = env->GetFieldID(clazz, "mMode", "I")) == NULL ||
- env->RegisterNatives(clazz, gMethods, NELEM(gMethods)) < 0) {
- ALOGE("JNI registration failed");
- return -1;
- }
- return 0;
-}
diff --git a/voip/jni/rtp/EchoSuppressor.cpp b/voip/jni/rtp/EchoSuppressor.cpp
deleted file mode 100644
index e223136..0000000
--- a/voip/jni/rtp/EchoSuppressor.cpp
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyrightm (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <stdint.h>
-#include <string.h>
-#include <math.h>
-
-#define LOG_TAG "Echo"
-#include <utils/Log.h>
-
-#include "EchoSuppressor.h"
-
-// It is very difficult to do echo cancellation at this level due to the lack of
-// the timing information of the samples being played and recorded. Therefore,
-// for the first release only echo suppression is implemented.
-
-// The algorithm is derived from the "previous works" summarized in
-// A new class of doubletalk detectors based on cross-correlation,
-// J Benesty, DR Morgan, JH Cho, IEEE Trans. on Speech and Audio Processing.
-// The method proposed in that paper is not used because of its high complexity.
-
-// It is well known that cross-correlation can be computed using convolution,
-// but unfortunately not every mobile processor has a (fast enough) FPU. Thus
-// we use integer arithmetic as much as possible and do lots of bookkeeping.
-// Again, parameters and thresholds are chosen by experiments.
-
-EchoSuppressor::EchoSuppressor(int sampleCount, int tailLength)
-{
- tailLength += sampleCount * 4;
-
- int shift = 0;
- while ((sampleCount >> shift) > 1 && (tailLength >> shift) > 256) {
- ++shift;
- }
-
- mShift = shift + 4;
- mScale = 1 << shift;
- mSampleCount = sampleCount;
- mWindowSize = sampleCount >> shift;
- mTailLength = tailLength >> shift;
- mRecordLength = tailLength * 2 / sampleCount;
- mRecordOffset = 0;
-
- mXs = new uint16_t[mTailLength + mWindowSize];
- memset(mXs, 0, sizeof(*mXs) * (mTailLength + mWindowSize));
- mXSums = new uint32_t[mTailLength];
- memset(mXSums, 0, sizeof(*mXSums) * mTailLength);
- mX2Sums = new uint32_t[mTailLength];
- memset(mX2Sums, 0, sizeof(*mX2Sums) * mTailLength);
- mXRecords = new uint16_t[mRecordLength * mWindowSize];
- memset(mXRecords, 0, sizeof(*mXRecords) * mRecordLength * mWindowSize);
-
- mYSum = 0;
- mY2Sum = 0;
- mYRecords = new uint32_t[mRecordLength];
- memset(mYRecords, 0, sizeof(*mYRecords) * mRecordLength);
- mY2Records = new uint32_t[mRecordLength];
- memset(mY2Records, 0, sizeof(*mY2Records) * mRecordLength);
-
- mXYSums = new uint32_t[mTailLength];
- memset(mXYSums, 0, sizeof(*mXYSums) * mTailLength);
- mXYRecords = new uint32_t[mRecordLength * mTailLength];
- memset(mXYRecords, 0, sizeof(*mXYRecords) * mRecordLength * mTailLength);
-
- mLastX = 0;
- mLastY = 0;
- mWeight = 1.0f / (mRecordLength * mWindowSize);
-}
-
-EchoSuppressor::~EchoSuppressor()
-{
- delete [] mXs;
- delete [] mXSums;
- delete [] mX2Sums;
- delete [] mXRecords;
- delete [] mYRecords;
- delete [] mY2Records;
- delete [] mXYSums;
- delete [] mXYRecords;
-}
-
-void EchoSuppressor::run(int16_t *playbacked, int16_t *recorded)
-{
- // Update Xs.
- for (int i = mTailLength - 1; i >= 0; --i) {
- mXs[i + mWindowSize] = mXs[i];
- }
- for (int i = mWindowSize - 1, j = 0; i >= 0; --i, j += mScale) {
- uint32_t sum = 0;
- for (int k = 0; k < mScale; ++k) {
- int32_t x = playbacked[j + k] << 15;
- mLastX += x;
- sum += ((mLastX >= 0) ? mLastX : -mLastX) >> 15;
- mLastX -= (mLastX >> 10) + x;
- }
- mXs[i] = sum >> mShift;
- }
-
- // Update XSums, X2Sums, and XRecords.
- for (int i = mTailLength - mWindowSize - 1; i >= 0; --i) {
- mXSums[i + mWindowSize] = mXSums[i];
- mX2Sums[i + mWindowSize] = mX2Sums[i];
- }
- uint16_t *xRecords = &mXRecords[mRecordOffset * mWindowSize];
- for (int i = mWindowSize - 1; i >= 0; --i) {
- uint16_t x = mXs[i];
- mXSums[i] = mXSums[i + 1] + x - xRecords[i];
- mX2Sums[i] = mX2Sums[i + 1] + x * x - xRecords[i] * xRecords[i];
- xRecords[i] = x;
- }
-
- // Compute Ys.
- uint16_t ys[mWindowSize];
- for (int i = mWindowSize - 1, j = 0; i >= 0; --i, j += mScale) {
- uint32_t sum = 0;
- for (int k = 0; k < mScale; ++k) {
- int32_t y = recorded[j + k] << 15;
- mLastY += y;
- sum += ((mLastY >= 0) ? mLastY : -mLastY) >> 15;
- mLastY -= (mLastY >> 10) + y;
- }
- ys[i] = sum >> mShift;
- }
-
- // Update YSum, Y2Sum, YRecords, and Y2Records.
- uint32_t ySum = 0;
- uint32_t y2Sum = 0;
- for (int i = mWindowSize - 1; i >= 0; --i) {
- ySum += ys[i];
- y2Sum += ys[i] * ys[i];
- }
- mYSum += ySum - mYRecords[mRecordOffset];
- mY2Sum += y2Sum - mY2Records[mRecordOffset];
- mYRecords[mRecordOffset] = ySum;
- mY2Records[mRecordOffset] = y2Sum;
-
- // Update XYSums and XYRecords.
- uint32_t *xyRecords = &mXYRecords[mRecordOffset * mTailLength];
- for (int i = mTailLength - 1; i >= 0; --i) {
- uint32_t xySum = 0;
- for (int j = mWindowSize - 1; j >= 0; --j) {
- xySum += mXs[i + j] * ys[j];
- }
- mXYSums[i] += xySum - xyRecords[i];
- xyRecords[i] = xySum;
- }
-
- // Compute correlations.
- int latency = 0;
- float corr2 = 0.0f;
- float varX = 0.0f;
- float varY = mY2Sum - mWeight * mYSum * mYSum;
- for (int i = mTailLength - 1; i >= 0; --i) {
- float cov = mXYSums[i] - mWeight * mXSums[i] * mYSum;
- if (cov > 0.0f) {
- float varXi = mX2Sums[i] - mWeight * mXSums[i] * mXSums[i];
- float corr2i = cov * cov / (varXi * varY + 1);
- if (corr2i > corr2) {
- varX = varXi;
- corr2 = corr2i;
- latency = i;
- }
- }
- }
- //ALOGI("corr^2 %.5f, var %8.0f %8.0f, latency %d", corr2, varX, varY,
- // latency * mScale);
-
- // Do echo suppression.
- if (corr2 > 0.1f && varX > 10000.0f) {
- int factor = (corr2 > 1.0f) ? 0 : (1.0f - sqrtf(corr2)) * 4096;
- for (int i = 0; i < mSampleCount; ++i) {
- recorded[i] = recorded[i] * factor >> 16;
- }
- }
-
- // Increase RecordOffset.
- ++mRecordOffset;
- if (mRecordOffset == mRecordLength) {
- mRecordOffset = 0;
- }
-}
diff --git a/voip/jni/rtp/EchoSuppressor.h b/voip/jni/rtp/EchoSuppressor.h
deleted file mode 100644
index 2f3b593..0000000
--- a/voip/jni/rtp/EchoSuppressor.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyrightm (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __ECHO_SUPPRESSOR_H__
-#define __ECHO_SUPPRESSOR_H__
-
-#include <stdint.h>
-
-class EchoSuppressor
-{
-public:
- // The sampleCount must be power of 2.
- EchoSuppressor(int sampleCount, int tailLength);
- ~EchoSuppressor();
- void run(int16_t *playbacked, int16_t *recorded);
-
-private:
- int mShift;
- int mScale;
- int mSampleCount;
- int mWindowSize;
- int mTailLength;
- int mRecordLength;
- int mRecordOffset;
-
- uint16_t *mXs;
- uint32_t *mXSums;
- uint32_t *mX2Sums;
- uint16_t *mXRecords;
-
- uint32_t mYSum;
- uint32_t mY2Sum;
- uint32_t *mYRecords;
- uint32_t *mY2Records;
-
- uint32_t *mXYSums;
- uint32_t *mXYRecords;
-
- int32_t mLastX;
- int32_t mLastY;
-
- float mWeight;
-};
-
-#endif
diff --git a/voip/jni/rtp/G711Codec.cpp b/voip/jni/rtp/G711Codec.cpp
deleted file mode 100644
index ef54863..0000000
--- a/voip/jni/rtp/G711Codec.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyrightm (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "AudioCodec.h"
-
-namespace {
-
-const int8_t gExponents[128] = {
- 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
-};
-
-//------------------------------------------------------------------------------
-
-class UlawCodec : public AudioCodec
-{
-public:
- int set(int sampleRate, const char *fmtp) {
- mSampleCount = sampleRate / 50;
- return mSampleCount;
- }
- int encode(void *payload, int16_t *samples);
- int decode(int16_t *samples, int count, void *payload, int length);
-private:
- int mSampleCount;
-};
-
-int UlawCodec::encode(void *payload, int16_t *samples)
-{
- int8_t *ulaws = (int8_t *)payload;
- for (int i = 0; i < mSampleCount; ++i) {
- int sample = samples[i];
- int sign = (sample >> 8) & 0x80;
- if (sample < 0) {
- sample = -sample;
- }
- sample += 132;
- if (sample > 32767) {
- sample = 32767;
- }
- int exponent = gExponents[sample >> 8];
- int mantissa = (sample >> (exponent + 3)) & 0x0F;
- ulaws[i] = ~(sign | (exponent << 4) | mantissa);
- }
- return mSampleCount;
-}
-
-int UlawCodec::decode(int16_t *samples, int count, void *payload, int length)
-{
- int8_t *ulaws = (int8_t *)payload;
- if (length > count) {
- length = count;
- }
- for (int i = 0; i < length; ++i) {
- int ulaw = ~ulaws[i];
- int exponent = (ulaw >> 4) & 0x07;
- int mantissa = ulaw & 0x0F;
- int sample = (((mantissa << 3) + 132) << exponent) - 132;
- samples[i] = (ulaw < 0 ? -sample : sample);
- }
- return length;
-}
-
-//------------------------------------------------------------------------------
-
-class AlawCodec : public AudioCodec
-{
-public:
- int set(int sampleRate, const char *fmtp) {
- mSampleCount = sampleRate / 50;
- return mSampleCount;
- }
- int encode(void *payload, int16_t *samples);
- int decode(int16_t *samples, int count, void *payload, int length);
-private:
- int mSampleCount;
-};
-
-int AlawCodec::encode(void *payload, int16_t *samples)
-{
- int8_t *alaws = (int8_t *)payload;
- for (int i = 0; i < mSampleCount; ++i) {
- int sample = samples[i];
- int sign = (sample >> 8) & 0x80;
- if (sample < 0) {
- sample = -sample;
- }
- if (sample > 32767) {
- sample = 32767;
- }
- int exponent = gExponents[sample >> 8];
- int mantissa = (sample >> (exponent == 0 ? 4 : exponent + 3)) & 0x0F;
- alaws[i] = (sign | (exponent << 4) | mantissa) ^ 0xD5;
- }
- return mSampleCount;
-}
-
-int AlawCodec::decode(int16_t *samples, int count, void *payload, int length)
-{
- int8_t *alaws = (int8_t *)payload;
- if (length > count) {
- length = count;
- }
- for (int i = 0; i < length; ++i) {
- int alaw = alaws[i] ^ 0x55;
- int exponent = (alaw >> 4) & 0x07;
- int mantissa = alaw & 0x0F;
- int sample = (exponent == 0 ? (mantissa << 4) + 8 :
- ((mantissa << 3) + 132) << exponent);
- samples[i] = (alaw < 0 ? sample : -sample);
- }
- return length;
-}
-
-} // namespace
-
-AudioCodec *newUlawCodec()
-{
- return new UlawCodec;
-}
-
-AudioCodec *newAlawCodec()
-{
- return new AlawCodec;
-}
diff --git a/voip/jni/rtp/GsmCodec.cpp b/voip/jni/rtp/GsmCodec.cpp
deleted file mode 100644
index 61dfdc9..0000000
--- a/voip/jni/rtp/GsmCodec.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyrightm (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "AudioCodec.h"
-
-extern "C" {
-#include "gsm.h"
-}
-
-namespace {
-
-class GsmCodec : public AudioCodec
-{
-public:
- GsmCodec() {
- mEncode = gsm_create();
- mDecode = gsm_create();
- }
-
- ~GsmCodec() {
- if (mEncode) {
- gsm_destroy(mEncode);
- }
- if (mDecode) {
- gsm_destroy(mDecode);
- }
- }
-
- int set(int sampleRate, const char *fmtp) {
- return (sampleRate == 8000 && mEncode && mDecode) ? 160 : -1;
- }
-
- int encode(void *payload, int16_t *samples);
- int decode(int16_t *samples, int count, void *payload, int length);
-
-private:
- gsm mEncode;
- gsm mDecode;
-};
-
-int GsmCodec::encode(void *payload, int16_t *samples)
-{
- gsm_encode(mEncode, samples, (unsigned char *)payload);
- return 33;
-}
-
-int GsmCodec::decode(int16_t *samples, int count, void *payload, int length)
-{
- unsigned char *bytes = (unsigned char *)payload;
- int n = 0;
- while (n + 160 <= count && length >= 33 &&
- gsm_decode(mDecode, bytes, &samples[n]) == 0) {
- n += 160;
- length -= 33;
- bytes += 33;
- }
- return n;
-}
-
-} // namespace
-
-AudioCodec *newGsmCodec()
-{
- return new GsmCodec;
-}
diff --git a/voip/jni/rtp/RtpStream.cpp b/voip/jni/rtp/RtpStream.cpp
deleted file mode 100644
index bfe8e24..0000000
--- a/voip/jni/rtp/RtpStream.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-
-#define LOG_TAG "RtpStream"
-#include <utils/Log.h>
-
-#include "jni.h"
-#include "JNIHelp.h"
-
-extern int parse(JNIEnv *env, jstring jAddress, int port, sockaddr_storage *ss);
-
-namespace {
-
-jfieldID gSocket;
-
-jint create(JNIEnv *env, jobject thiz, jstring jAddress)
-{
- env->SetIntField(thiz, gSocket, -1);
-
- sockaddr_storage ss;
- if (parse(env, jAddress, 0, &ss) < 0) {
- // Exception already thrown.
- return -1;
- }
-
- int socket = ::socket(ss.ss_family, SOCK_DGRAM, 0);
- socklen_t len = sizeof(ss);
- if (socket == -1 || bind(socket, (sockaddr *)&ss, sizeof(ss)) != 0 ||
- getsockname(socket, (sockaddr *)&ss, &len) != 0) {
- jniThrowException(env, "java/net/SocketException", strerror(errno));
- ::close(socket);
- return -1;
- }
-
- uint16_t *p = (ss.ss_family == AF_INET) ?
- &((sockaddr_in *)&ss)->sin_port : &((sockaddr_in6 *)&ss)->sin6_port;
- uint16_t port = ntohs(*p);
- if ((port & 1) == 0) {
- env->SetIntField(thiz, gSocket, socket);
- return port;
- }
- ::close(socket);
-
- socket = ::socket(ss.ss_family, SOCK_DGRAM, 0);
- if (socket != -1) {
- uint16_t delta = port << 1;
- ++port;
-
- for (int i = 0; i < 1000; ++i) {
- do {
- port += delta;
- } while (port < 1024);
- *p = htons(port);
-
- if (bind(socket, (sockaddr *)&ss, sizeof(ss)) == 0) {
- env->SetIntField(thiz, gSocket, socket);
- return port;
- }
- }
- }
-
- jniThrowException(env, "java/net/SocketException", strerror(errno));
- ::close(socket);
- return -1;
-}
-
-void close(JNIEnv *env, jobject thiz)
-{
- int socket = env->GetIntField(thiz, gSocket);
- ::close(socket);
- env->SetIntField(thiz, gSocket, -1);
-}
-
-JNINativeMethod gMethods[] = {
- {"create", "(Ljava/lang/String;)I", (void *)create},
- {"close", "()V", (void *)close},
-};
-
-} // namespace
-
-int registerRtpStream(JNIEnv *env)
-{
- jclass clazz;
- if ((clazz = env->FindClass("android/net/rtp/RtpStream")) == NULL ||
- (gSocket = env->GetFieldID(clazz, "mSocket", "I")) == NULL ||
- env->RegisterNatives(clazz, gMethods, NELEM(gMethods)) < 0) {
- ALOGE("JNI registration failed");
- return -1;
- }
- return 0;
-}
diff --git a/voip/jni/rtp/rtp_jni.cpp b/voip/jni/rtp/rtp_jni.cpp
deleted file mode 100644
index 9f4bff9..0000000
--- a/voip/jni/rtp/rtp_jni.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-
-#include "jni.h"
-
-extern int registerRtpStream(JNIEnv *env);
-extern int registerAudioGroup(JNIEnv *env);
-
-__attribute__((visibility("default"))) jint JNI_OnLoad(JavaVM *vm, void *unused)
-{
- JNIEnv *env = NULL;
- if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK ||
- registerRtpStream(env) < 0 || registerAudioGroup(env) < 0) {
- return -1;
- }
- return JNI_VERSION_1_4;
-}
diff --git a/voip/jni/rtp/util.cpp b/voip/jni/rtp/util.cpp
deleted file mode 100644
index 1d702fc..0000000
--- a/voip/jni/rtp/util.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-
-#include "jni.h"
-#include "JNIHelp.h"
-
-int parse(JNIEnv *env, jstring jAddress, int port, sockaddr_storage *ss)
-{
- if (!jAddress) {
- jniThrowNullPointerException(env, "address");
- return -1;
- }
- if (port < 0 || port > 65535) {
- jniThrowException(env, "java/lang/IllegalArgumentException", "port");
- return -1;
- }
- const char *address = env->GetStringUTFChars(jAddress, NULL);
- if (!address) {
- // Exception already thrown.
- return -1;
- }
- memset(ss, 0, sizeof(*ss));
-
- sockaddr_in *sin = (sockaddr_in *)ss;
- if (inet_pton(AF_INET, address, &(sin->sin_addr)) > 0) {
- sin->sin_family = AF_INET;
- sin->sin_port = htons(port);
- env->ReleaseStringUTFChars(jAddress, address);
- return 0;
- }
-
- sockaddr_in6 *sin6 = (sockaddr_in6 *)ss;
- if (inet_pton(AF_INET6, address, &(sin6->sin6_addr)) > 0) {
- sin6->sin6_family = AF_INET6;
- sin6->sin6_port = htons(port);
- env->ReleaseStringUTFChars(jAddress, address);
- return 0;
- }
-
- env->ReleaseStringUTFChars(jAddress, address);
- jniThrowException(env, "java/lang/IllegalArgumentException", "address");
- return -1;
-}