Merge "Adding quaternion functions to math library."
diff --git a/Android.mk b/Android.mk
index 7c29c73..524ec51 100644
--- a/Android.mk
+++ b/Android.mk
@@ -368,7 +368,7 @@
-since ./frameworks/base/api/7.xml 7 \
-since ./frameworks/base/api/8.xml 8 \
-since ./frameworks/base/api/current.xml HC \
- -error 1 -error 2 -warning 3 -error 4 -error 6 -error 8 \
+ -error 101 -error 102 -warning 103 -error 104 -error 106 -error 108 \
-overview $(LOCAL_PATH)/core/java/overview.html
framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:=$(call intermediates-dir-for,JAVA_LIBRARIES,framework)
diff --git a/api/current.xml b/api/current.xml
index 61c1cac..a9ef54e 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -26353,6 +26353,17 @@
<parameter name="id" type="int">
</parameter>
</method>
+<method name="getActionBar"
+ return="android.app.ActionBar"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getContext"
return="android.content.Context"
abstract="false"
@@ -30658,6 +30669,17 @@
<parameter name="holder" type="android.view.SurfaceHolder">
</parameter>
</method>
+<field name="KEY_NATIVE_SAVED_STATE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android:native_state""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="META_DATA_LIB_NAME"
type="java.lang.String"
transient="false"
@@ -61912,6 +61934,21 @@
<parameter name="sqlString" type="java.lang.String">
</parameter>
</method>
+<method name="appendSelectionArgs"
+ return="java.lang.String[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="originalValues" type="java.lang.String[]">
+</parameter>
+<parameter name="newValues" type="java.lang.String[]">
+</parameter>
+</method>
<method name="appendValueToSql"
return="void"
abstract="false"
@@ -91791,7 +91828,7 @@
type="int"
transient="false"
volatile="false"
- value="1"
+ value="0"
static="true"
final="true"
deprecated="not deprecated"
@@ -91802,7 +91839,7 @@
type="int"
transient="false"
volatile="false"
- value="0"
+ value="1"
static="true"
final="true"
deprecated="not deprecated"
@@ -104563,6 +104600,21 @@
visibility="public"
>
</method>
+<method name="getBooleanQueryParameter"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="key" type="java.lang.String">
+</parameter>
+<parameter name="defaultValue" type="boolean">
+</parameter>
+</method>
<method name="getEncodedAuthority"
return="java.lang.String"
abstract="true"
@@ -193670,6 +193722,28 @@
visibility="public"
>
</method>
+<method name="getRotationX"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getRotationY"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getScaleX"
return="float"
abstract="false"
@@ -195840,6 +195914,32 @@
<parameter name="rotation" type="float">
</parameter>
</method>
+<method name="setRotationX"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="rotationX" type="float">
+</parameter>
+</method>
+<method name="setRotationY"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="rotationY" type="float">
+</parameter>
+</method>
<method name="setSaveEnabled"
return="void"
abstract="false"
@@ -213236,17 +213336,6 @@
visibility="public"
>
</method>
-<method name="getVisibleTitleHeight"
- return="int"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
<method name="getZoomControls"
return="android.view.View"
abstract="false"
@@ -216570,7 +216659,7 @@
</interface>
<class name="AdapterViewAnimator"
extends="android.widget.AdapterView"
- abstract="false"
+ abstract="true"
static="false"
final="false"
deprecated="not deprecated"
@@ -216620,28 +216709,6 @@
visibility="public"
>
</method>
-<method name="getDefaultInAnimation"
- return="android.view.animation.Animation"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getDefaultOutAnimation"
- return="android.view.animation.Animation"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
<method name="getDisplayedChild"
return="int"
abstract="false"
@@ -216840,23 +216907,6 @@
visibility="public"
>
</method>
-<method name="showOnly"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="childIndex" type="int">
-</parameter>
-<parameter name="animate" type="boolean">
-</parameter>
-<parameter name="onLayout" type="boolean">
-</parameter>
-</method>
<method name="showPrevious"
return="void"
abstract="false"
@@ -228032,6 +228082,22 @@
<parameter name="autoRequery" type="boolean">
</parameter>
</constructor>
+<constructor name="ResourceCursorAdapter"
+ type="android.widget.ResourceCursorAdapter"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="layout" type="int">
+</parameter>
+<parameter name="c" type="android.database.Cursor">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</constructor>
<method name="newView"
return="android.view.View"
abstract="false"
@@ -230162,6 +230228,37 @@
</parameter>
</method>
</interface>
+<class name="StackView"
+ extends="android.widget.AdapterViewAnimator"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="StackView"
+ type="android.widget.StackView"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</constructor>
+<constructor name="StackView"
+ type="android.widget.StackView"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+</constructor>
+</class>
<class name="TabHost"
extends="android.widget.FrameLayout"
abstract="false"
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index d49adc2..3318bb1 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1748,13 +1748,11 @@
/**
* Retrieve a reference to this activity's ActionBar.
- *
- * <p><em>Note:</em> The ActionBar is initialized when a content view
- * is set. This function will return null if called before {@link #setContentView}
- * or {@link #addContentView}.
+ *
* @return The Activity's ActionBar, or null if it does not have one.
*/
public ActionBar getActionBar() {
+ initActionBar();
return mActionBar;
}
@@ -1764,7 +1762,7 @@
*/
private void initActionBar() {
Window window = getWindow();
- if (!window.hasFeature(Window.FEATURE_ACTION_BAR) || mActionBar != null) {
+ if (isChild() || !window.hasFeature(Window.FEATURE_ACTION_BAR) || mActionBar != null) {
return;
}
@@ -4093,6 +4091,7 @@
}
public ActionMode onStartActionMode(ActionMode.Callback callback) {
+ initActionBar();
if (mActionBar != null) {
return mActionBar.startActionMode(callback);
}
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index a9420b4e..b4c138e 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -16,6 +16,7 @@
package android.app;
+import com.android.internal.app.ActionBarImpl;
import com.android.internal.policy.PolicyManager;
import android.content.ComponentName;
@@ -27,9 +28,9 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.view.ActionMode;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
-import android.view.ActionMode;
import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.KeyEvent;
@@ -77,6 +78,7 @@
final WindowManager mWindowManager;
Window mWindow;
View mDecor;
+ private ActionBarImpl mActionBar;
/**
* This field should be made private, so it is hidden from the SDK.
* {@hide}
@@ -178,6 +180,15 @@
}
/**
+ * Retrieve the {@link ActionBar} attached to this dialog, if present.
+ *
+ * @return The ActionBar attached to the dialog or null if no ActionBar is present.
+ */
+ public ActionBar getActionBar() {
+ return mActionBar;
+ }
+
+ /**
* Sets the Activity that owns this dialog. An example use: This Dialog will
* use the suggested volume control stream of the Activity.
*
@@ -228,6 +239,11 @@
onStart();
mDecor = mWindow.getDecorView();
+
+ if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
+ mActionBar = new ActionBarImpl(this);
+ }
+
WindowManager.LayoutParams l = mWindow.getAttributes();
if ((l.softInputMode
& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
@@ -834,12 +850,14 @@
}
public ActionMode onStartActionMode(ActionMode.Callback callback) {
- // TODO Support context modes in dialogs
+ if (mActionBar != null) {
+ return mActionBar.startActionMode(callback);
+ }
return null;
}
/**
- * @return The activity associated with this dialog, or null if there is no assocaited activity.
+ * @return The activity associated with this dialog, or null if there is no associated activity.
*/
private ComponentName getAssociatedActivity() {
Activity activity = mOwnerActivity;
diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java
index e7bdd8b..28abcaa 100644
--- a/core/java/android/app/LoaderManager.java
+++ b/core/java/android/app/LoaderManager.java
@@ -185,11 +185,14 @@
void stop() {
if (DEBUG) Log.v(TAG, " Stopping: " + this);
mStarted = false;
- if (mLoader != null && mListenerRegistered) {
- // Let the loader know we're done with it
- mListenerRegistered = false;
- mLoader.unregisterListener(this);
- mLoader.stopLoading();
+ if (!mRetaining) {
+ if (mLoader != null && mListenerRegistered) {
+ // Let the loader know we're done with it
+ mListenerRegistered = false;
+ mLoader.unregisterListener(this);
+ mLoader.stopLoading();
+ }
+ mData = null;
}
}
diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java
index eaf0675..4dc88b3 100644
--- a/core/java/android/app/NativeActivity.java
+++ b/core/java/android/app/NativeActivity.java
@@ -10,6 +10,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
+import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.os.Build;
import android.os.Bundle;
@@ -32,12 +33,27 @@
/**
* Convenience for implementing an activity that will be implemented
- * purely in native code. That is, a game (or game-like thing).
+ * purely in native code. That is, a game (or game-like thing). There
+ * is no need to derive from this class; you can simply declare it in your
+ * manifest, and use the NDK APIs from there.
+ *
+ * <p>A typical manifest would look like:
+ *
+ * {@sample development/ndk/platforms/android-9/samples/native-activity/AndroidManifest.xml
+ * manifest}
+ *
+ * <p>A very simple example of native code that is run by NativeActivity
+ * follows. This reads input events from the user and uses OpenGLES to
+ * draw into the native activity's window.
+ *
+ * {@sample development/ndk/platforms/android-9/samples/native-activity/jni/main.c all}
*/
public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
InputQueue.Callback, OnGlobalLayoutListener {
public static final String META_DATA_LIB_NAME = "android.app.lib_name";
+ public static final String KEY_NATIVE_SAVED_STATE = "android:native_state";
+
private NativeContentView mNativeContentView;
private InputMethodManager mIMM;
private InputMethodCallback mInputMethodCallback;
@@ -59,14 +75,15 @@
private native int loadNativeCode(String path, MessageQueue queue,
String internalDataPath, String externalDataPath, int sdkVersion,
- AssetManager assetMgr);
+ AssetManager assetMgr, byte[] savedState);
private native void unloadNativeCode(int handle);
private native void onStartNative(int handle);
private native void onResumeNative(int handle);
- private native void onSaveInstanceStateNative(int handle);
+ private native byte[] onSaveInstanceStateNative(int handle);
private native void onPauseNative(int handle);
private native void onStopNative(int handle);
+ private native void onConfigurationChangedNative(int handle);
private native void onLowMemoryNative(int handle);
private native void onWindowFocusChangedNative(int handle, boolean focused);
private native void onSurfaceCreatedNative(int handle, Surface surface);
@@ -165,10 +182,13 @@
throw new IllegalArgumentException("Unable to find native library: " + libname);
}
+ byte[] nativeSavedState = savedInstanceState != null
+ ? savedInstanceState.getByteArray(KEY_NATIVE_SAVED_STATE) : null;
+
mNativeHandle = loadNativeCode(path, Looper.myQueue(),
getFilesDir().toString(),
Environment.getExternalStorageAppFilesDirectory(ai.packageName).toString(),
- Build.VERSION.SDK_INT, getAssets());
+ Build.VERSION.SDK_INT, getAssets(), nativeSavedState);
if (mNativeHandle == 0) {
throw new IllegalArgumentException("Unable to load native library: " + path);
@@ -206,7 +226,10 @@
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
- onSaveInstanceStateNative(mNativeHandle);
+ byte[] state = onSaveInstanceStateNative(mNativeHandle);
+ if (state != null) {
+ outState.putByteArray(KEY_NATIVE_SAVED_STATE, state);
+ }
}
@Override
@@ -222,6 +245,14 @@
}
@Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ if (!mDestroyed) {
+ onConfigurationChangedNative(mNativeHandle);
+ }
+ }
+
+ @Override
public void onLowMemory() {
super.onLowMemory();
if (!mDestroyed) {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 8eda844..03bcadc 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -468,12 +468,17 @@
* <p>Valid Bluetooth names are a maximum of 248 UTF-8 characters, however
* many remote devices can only display the first 40 characters, and some
* may be limited to just 20.
+ * <p>If Bluetooth state is not {@link #STATE_ON}, this API
+ * will return false. After turning on Bluetooth,
+ * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
+ * to get the updated value.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
*
* @param name a valid Bluetooth name
* @return true if the name was set, false otherwise
*/
public boolean setName(String name) {
+ if (getState() != STATE_ON) return false;
try {
return mService.setName(name);
} catch (RemoteException e) {Log.e(TAG, "", e);}
@@ -488,11 +493,16 @@
* {@link #SCAN_MODE_NONE},
* {@link #SCAN_MODE_CONNECTABLE},
* {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
+ * <p>If Bluetooth state is not {@link #STATE_ON}, this API
+ * will return {@link #SCAN_MODE_NONE}. After turning on Bluetooth,
+ * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
+ * to get the updated value.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
* @return scan mode
*/
public int getScanMode() {
+ if (getState() != STATE_ON) return SCAN_MODE_NONE;
try {
return mService.getScanMode();
} catch (RemoteException e) {Log.e(TAG, "", e);}
@@ -511,6 +521,10 @@
* {@link #SCAN_MODE_NONE},
* {@link #SCAN_MODE_CONNECTABLE},
* {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
+ * <p>If Bluetooth state is not {@link #STATE_ON}, this API
+ * will return false. After turning on Bluetooth,
+ * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
+ * to get the updated value.
* <p>Requires {@link android.Manifest.permission#WRITE_SECURE_SETTINGS}
* <p>Applications cannot set the scan mode. They should use
* <code>startActivityForResult(
@@ -524,6 +538,7 @@
* @hide
*/
public boolean setScanMode(int mode, int duration) {
+ if (getState() != STATE_ON) return false;
try {
return mService.setScanMode(mode, duration);
} catch (RemoteException e) {Log.e(TAG, "", e);}
@@ -532,11 +547,13 @@
/** @hide */
public boolean setScanMode(int mode) {
+ if (getState() != STATE_ON) return false;
return setScanMode(mode, 120);
}
/** @hide */
public int getDiscoverableTimeout() {
+ if (getState() != STATE_ON) return -1;
try {
return mService.getDiscoverableTimeout();
} catch (RemoteException e) {Log.e(TAG, "", e);}
@@ -545,6 +562,7 @@
/** @hide */
public void setDiscoverableTimeout(int timeout) {
+ if (getState() != STATE_ON) return;
try {
mService.setDiscoverableTimeout(timeout);
} catch (RemoteException e) {Log.e(TAG, "", e);}
@@ -572,11 +590,16 @@
* <p>Device discovery will only find remote devices that are currently
* <i>discoverable</i> (inquiry scan enabled). Many Bluetooth devices are
* not discoverable by default, and need to be entered into a special mode.
+ * <p>If Bluetooth state is not {@link #STATE_ON}, this API
+ * will return false. After turning on Bluetooth,
+ * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
+ * to get the updated value.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
*
* @return true on success, false on error
*/
public boolean startDiscovery() {
+ if (getState() != STATE_ON) return false;
try {
return mService.startDiscovery();
} catch (RemoteException e) {Log.e(TAG, "", e);}
@@ -593,10 +616,15 @@
* the Activity, but is run as a system service, so an application should
* always call cancel discovery even if it did not directly request a
* discovery, just to be sure.
+ * <p>If Bluetooth state is not {@link #STATE_ON}, this API
+ * will return false. After turning on Bluetooth,
+ * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
+ * to get the updated value.
*
* @return true on success, false on error
*/
public boolean cancelDiscovery() {
+ if (getState() != STATE_ON) return false;
try {
mService.cancelDiscovery();
} catch (RemoteException e) {Log.e(TAG, "", e);}
@@ -614,11 +642,16 @@
* <p>Applications can also register for {@link #ACTION_DISCOVERY_STARTED}
* or {@link #ACTION_DISCOVERY_FINISHED} to be notified when discovery
* starts or completes.
+ * <p>If Bluetooth state is not {@link #STATE_ON}, this API
+ * will return false. After turning on Bluetooth,
+ * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
+ * to get the updated value.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
*
* @return true if discovering
*/
public boolean isDiscovering() {
+ if (getState() != STATE_ON) return false;
try {
return mService.isDiscovering();
} catch (RemoteException e) {Log.e(TAG, "", e);}
@@ -628,11 +661,18 @@
/**
* Return the set of {@link BluetoothDevice} objects that are bonded
* (paired) to the local adapter.
+ * <p>If Bluetooth state is not {@link #STATE_ON}, this API
+ * will return an empty set. After turning on Bluetooth,
+ * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
+ * to get the updated value.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
*
* @return unmodifiable set of {@link BluetoothDevice}, or null on error
*/
public Set<BluetoothDevice> getBondedDevices() {
+ if (getState() != STATE_ON) {
+ return toDeviceSet(new String[0]);
+ }
try {
return toDeviceSet(mService.listBonds());
} catch (RemoteException e) {Log.e(TAG, "", e);}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 1163add..e1d431f 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -1001,7 +1001,7 @@
/**
* @hide -- until interface has proven itself
*
- * Call an provider-defined method. This can be used to implement
+ * Call a provider-defined method. This can be used to implement
* interfaces that are cheaper than using a Cursor.
*
* @param method Method name to call. Opaque to framework.
@@ -1013,29 +1013,26 @@
}
/**
- * Shuts down this instance of the ContentProvider. It is useful when writing tests that use
- * the ContentProvider.
+ * Implement this to shut down the ContentProvider instance. You can then
+ * invoke this method in unit tests.
+ *
* <p>
- * If a unittest starts the ContentProvider in its test(..() methods, it could run into sqlite
- * errors "disk I/O error" or "corruption" in the following scenario:
- * <ul>
- * <li>Say, there are 2 test methods in the unittest</li>
- * <li>test1() (or setUp()) causes ContentProvider object to be initialized and
- * assume it opens a database connection to "foo.db"</li>
- * <li>est1() completes and test2() starts</li>
- * <li>During the execution of test2() there will be 2 connections to "foo.db"</li>
- * <li>Different threads in the ContentProvider may have one of these two connection
- * handles. This is not a problem per se</li>
- * <li>But if the two threads with 2 database connections don't interact correctly,
- * there could be unexpected errors from sqlite</li>
- * <li>Some of those unexpected errros are "disk I/O error" or "corruption" error</li>
- * <li>Common practice in tearDown() is to delete test directory (and the database files)</li>
- * <li>If this is done while some threads are still holding unclosed database connections,
- * sqlite quite easily gets into corruption and disk I/O errors</li>
- * </ul>
+ * Android normally handles ContentProvider startup and shutdown
+ * automatically. You do not need to start up or shut down a
+ * ContentProvider. When you invoke a test method on a ContentProvider,
+ * however, a ContentProvider instance is started and keeps running after
+ * the test finishes, even if a succeeding test instantiates another
+ * ContentProvider. A conflict develops because the two instances are
+ * usually running against the same underlying data source (for example, an
+ * sqlite database).
+ * </p>
* <p>
- * tearDown() in the unittests should call this method to have ContentProvider gracefully
- * shutdown all database connections.
+ * Implementing shutDown() avoids this conflict by providing a way to
+ * terminate the ContentProvider. This method can also prevent memory leaks
+ * from multiple instantiations of the ContentProvider, and it can ensure
+ * unit test isolation by allowing you to completely clean up the test
+ * fixture before moving on to the next test.
+ * </p>
*/
public void shutdown() {
Log.w(TAG, "implement ContentProvider shutdown() to make sure all database " +
diff --git a/core/java/android/content/CursorLoader.java b/core/java/android/content/CursorLoader.java
index 850ff7f..42599ed 100644
--- a/core/java/android/content/CursorLoader.java
+++ b/core/java/android/content/CursorLoader.java
@@ -100,8 +100,8 @@
public void stopLoading() {
if (mCursor != null && !mCursor.isClosed()) {
mCursor.close();
- mCursor = null;
}
+ mCursor = null;
// Attempt to cancel the current load task if possible.
cancelLoad();
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index ba1b3a9..812af5a 100755
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1333,7 +1333,7 @@
height = mMetrics.widthPixels;
}
int keyboardHidden = mConfiguration.keyboardHidden;
- if (keyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO
+ if (keyboardHidden == Configuration.KEYBOARDHIDDEN_NO
&& mConfiguration.hardKeyboardHidden
== Configuration.HARDKEYBOARDHIDDEN_YES) {
keyboardHidden = Configuration.KEYBOARDHIDDEN_SOFT;
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index 9ac45d8..c07c3c6 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -1203,4 +1203,18 @@
}
return STATEMENT_OTHER;
}
+
+ /**
+ * Appends one set of selection args to another. This is useful when adding a selection
+ * argument to a user provided set.
+ */
+ public static String[] appendSelectionArgs(String[] originalValues, String[] newValues) {
+ if (originalValues == null || originalValues.length == 0) {
+ return newValues;
+ }
+ String[] result = new String[originalValues.length + newValues.length ];
+ System.arraycopy(originalValues, 0, result, 0, originalValues.length);
+ System.arraycopy(newValues, 0, result, originalValues.length, newValues.length);
+ return result;
+ }
}
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 11cd526..efbccd2 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -526,12 +526,6 @@
return -1;
}
- /**
- * This is not supported.
- */
- public void interpretScanResultsAvailable() {
- }
-
@Override
public String toString() {
StringBuffer sb = new StringBuffer("Mobile data state: ");
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index 44215e7..82735e5 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -27,18 +27,17 @@
public interface NetworkStateTracker {
public static final int EVENT_STATE_CHANGED = 1;
- public static final int EVENT_SCAN_RESULTS_AVAILABLE = 2;
/**
* arg1: 1 to show, 0 to hide
* arg2: ID of the notification
* obj: Notification (if showing)
*/
- public static final int EVENT_NOTIFICATION_CHANGED = 3;
- public static final int EVENT_CONFIGURATION_CHANGED = 4;
- public static final int EVENT_ROAMING_CHANGED = 5;
- public static final int EVENT_NETWORK_SUBTYPE_CHANGED = 6;
- public static final int EVENT_RESTORE_DEFAULT_NETWORK = 7;
- public static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK = 8;
+ public static final int EVENT_NOTIFICATION_CHANGED = 2;
+ public static final int EVENT_CONFIGURATION_CHANGED = 3;
+ public static final int EVENT_ROAMING_CHANGED = 4;
+ public static final int EVENT_NETWORK_SUBTYPE_CHANGED = 5;
+ public static final int EVENT_RESTORE_DEFAULT_NETWORK = 6;
+ public static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK = 7;
/**
* Fetch NetworkInfo for the network
@@ -147,10 +146,4 @@
*/
public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid);
- /**
- * Interprets scan results. This will be called at a safe time for
- * processing, and from a safe thread.
- */
- public void interpretScanResultsAvailable();
-
}
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 47faaba..63adcd0 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -1568,7 +1568,7 @@
throw new UnsupportedOperationException(NOT_HIERARCHICAL);
}
if (key == null) {
- throw new NullPointerException("key");
+ throw new NullPointerException("key");
}
final String query = getEncodedQuery();
@@ -1608,6 +1608,24 @@
return null;
}
+ /**
+ * Searches the query string for the first value with the given key and interprets it
+ * as a boolean value. "false" and "0" are interpreted as <code>false</code>, everything
+ * else is interpreted as <code>true</code>.
+ *
+ * @param key which will be decoded
+ * @param defaultValue the default value to return if there is no query parameter for key
+ * @return the boolean interpretation of the query parameter key
+ */
+ public boolean getBooleanQueryParameter(String key, boolean defaultValue) {
+ String flag = getQueryParameter(key);
+ if (flag == null) {
+ return defaultValue;
+ }
+ flag = flag.toLowerCase();
+ return (!"false".equals(flag) && !"0".equals(flag));
+ }
+
/** Identifies a null parcelled Uri. */
private static final int NULL_TYPE_ID = 0;
diff --git a/core/java/android/provider/Mtp.java b/core/java/android/provider/Mtp.java
index 15f8666..bc764ac 100644
--- a/core/java/android/provider/Mtp.java
+++ b/core/java/android/provider/Mtp.java
@@ -308,6 +308,13 @@
public static final int FORMAT_ABSTRACT_CONTACT = 0xBB81;
public static final int FORMAT_VCARD_2 = 0xBB82;
+ // Object properties we support
+ public static final int PROPERTY_STORAGE_ID = 0xDC01;
+ public static final int PROPERTY_OBJECT_FORMAT = 0xDC02;
+ public static final int PROPERTY_OBJECT_SIZE = 0xDC04;
+ public static final int PROPERTY_OBJECT_FILE_NAME = 0xDC07;
+ public static final int PROPERTY_PARENT_OBJECT = 0xDC0B;
+
/**
* Object is not protected. It may be modified and deleted, and its properties
* may be modified.
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 96bd884..91dbe1f 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -527,11 +527,18 @@
@Override
public void drawPath(Path path, Paint paint) {
boolean hasModifier = setupModifiers(paint);
- nDrawPath(mRenderer, path.mNativePath, paint.mNativePaint);
+ if (path.isSimplePath) {
+ if (path.rects != null) {
+ nDrawRects(mRenderer, path.rects.mNativeRegion, paint.mNativePaint);
+ }
+ } else {
+ nDrawPath(mRenderer, path.mNativePath, paint.mNativePaint);
+ }
if (hasModifier) nResetModifiers(mRenderer);
}
private native void nDrawPath(int renderer, int path, int paint);
+ private native void nDrawRects(int renderer, int region, int paint);
@Override
public void drawPicture(Picture picture) {
@@ -610,9 +617,13 @@
if ((index | count | (index + count) | (text.length - index - count)) < 0) {
throw new IndexOutOfBoundsException();
}
+
boolean hasModifier = setupModifiers(paint);
- nDrawText(mRenderer, text, index, count, x, y, paint.mBidiFlags, paint.mNativePaint);
- if (hasModifier) nResetModifiers(mRenderer);
+ try {
+ nDrawText(mRenderer, text, index, count, x, y, paint.mBidiFlags, paint.mNativePaint);
+ } finally {
+ if (hasModifier) nResetModifiers(mRenderer);
+ }
}
private native void nDrawText(int renderer, char[] text, int index, int count, float x, float y,
@@ -621,20 +632,23 @@
@Override
public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) {
boolean hasModifier = setupModifiers(paint);
- if (text instanceof String || text instanceof SpannedString ||
- text instanceof SpannableString) {
- nDrawText(mRenderer, text.toString(), start, end, x, y, paint.mBidiFlags,
- paint.mNativePaint);
- } else if (text instanceof GraphicsOperations) {
- ((GraphicsOperations) text).drawText(this, start, end, x, y,
- paint);
- } else {
- char[] buf = TemporaryBuffer.obtain(end - start);
- TextUtils.getChars(text, start, end, buf, 0);
- nDrawText(mRenderer, buf, 0, end - start, x, y, paint.mBidiFlags, paint.mNativePaint);
- TemporaryBuffer.recycle(buf);
+ try {
+ if (text instanceof String || text instanceof SpannedString ||
+ text instanceof SpannableString) {
+ nDrawText(mRenderer, text.toString(), start, end, x, y, paint.mBidiFlags,
+ paint.mNativePaint);
+ } else if (text instanceof GraphicsOperations) {
+ ((GraphicsOperations) text).drawText(this, start, end, x, y,
+ paint);
+ } else {
+ char[] buf = TemporaryBuffer.obtain(end - start);
+ TextUtils.getChars(text, start, end, buf, 0);
+ nDrawText(mRenderer, buf, 0, end - start, x, y, paint.mBidiFlags, paint.mNativePaint);
+ TemporaryBuffer.recycle(buf);
+ }
+ } finally {
+ if (hasModifier) nResetModifiers(mRenderer);
}
- if (hasModifier) nResetModifiers(mRenderer);
}
@Override
@@ -642,9 +656,13 @@
if ((start | end | (end - start) | (text.length() - end)) < 0) {
throw new IndexOutOfBoundsException();
}
+
boolean hasModifier = setupModifiers(paint);
- nDrawText(mRenderer, text, start, end, x, y, paint.mBidiFlags, paint.mNativePaint);
- if (hasModifier) nResetModifiers(mRenderer);
+ try {
+ nDrawText(mRenderer, text, start, end, x, y, paint.mBidiFlags, paint.mNativePaint);
+ } finally {
+ if (hasModifier) nResetModifiers(mRenderer);
+ }
}
private native void nDrawText(int renderer, String text, int start, int end, float x, float y,
@@ -653,8 +671,12 @@
@Override
public void drawText(String text, float x, float y, Paint paint) {
boolean hasModifier = setupModifiers(paint);
- nDrawText(mRenderer, text, 0, text.length(), x, y, paint.mBidiFlags, paint.mNativePaint);
- if (hasModifier) nResetModifiers(mRenderer);
+ try {
+ nDrawText(mRenderer, text, 0, text.length(), x, y, paint.mBidiFlags,
+ paint.mNativePaint);
+ } finally {
+ if (hasModifier) nResetModifiers(mRenderer);
+ }
}
@Override
@@ -671,15 +693,59 @@
@Override
public void drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount,
float x, float y, int dir, Paint paint) {
- throw new UnsupportedOperationException();
+ if ((index | count | text.length - index - count) < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ if (dir != DIRECTION_LTR && dir != DIRECTION_RTL) {
+ throw new IllegalArgumentException("Unknown direction: " + dir);
+ }
+
+ boolean hasModifier = setupModifiers(paint);
+ try {
+ nDrawTextRun(mRenderer, text, index, count, contextIndex, contextCount, x, y, dir,
+ paint.mNativePaint);
+ } finally {
+ if (hasModifier) nResetModifiers(mRenderer);
+ }
}
+ private native void nDrawTextRun(int renderer, char[] text, int index, int count,
+ int contextIndex, int contextCount, float x, float y, int dir, int nativePaint);
+
@Override
public void drawTextRun(CharSequence text, int start, int end, int contextStart, int contextEnd,
float x, float y, int dir, Paint paint) {
- throw new UnsupportedOperationException();
+ if ((start | end | end - start | text.length() - end) < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ boolean hasModifier = setupModifiers(paint);
+ try {
+ int flags = dir == 0 ? 0 : 1;
+ if (text instanceof String || text instanceof SpannedString ||
+ text instanceof SpannableString) {
+ nDrawTextRun(mRenderer, text.toString(), start, end, contextStart,
+ contextEnd, x, y, flags, paint.mNativePaint);
+ } else if (text instanceof GraphicsOperations) {
+ ((GraphicsOperations) text).drawTextRun(this, start, end,
+ contextStart, contextEnd, x, y, flags, paint);
+ } else {
+ int contextLen = contextEnd - contextStart;
+ int len = end - start;
+ char[] buf = TemporaryBuffer.obtain(contextLen);
+ TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
+ nDrawTextRun(mRenderer, buf, start - contextStart, len, 0, contextLen,
+ x, y, flags, paint.mNativePaint);
+ TemporaryBuffer.recycle(buf);
+ }
+ } finally {
+ if (hasModifier) nResetModifiers(mRenderer);
+ }
}
+ private native void nDrawTextRun(int renderer, String text, int start, int end,
+ int contextStart, int contextEnd, float x, float y, int flags, int nativePaint);
+
@Override
public void drawVertices(VertexMode mode, int vertexCount, float[] verts, int vertOffset,
float[] texs, int texOffset, int[] colors, int colorOffset, short[] indices,
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 60d495f..cd6b820 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -34,12 +34,23 @@
*
* @hide
*/
-abstract class HardwareRenderer {
+public abstract class HardwareRenderer {
private boolean mEnabled;
private boolean mRequested = true;
private static final String LOG_TAG = "HardwareRenderer";
/**
+ * Indicates whether hardware acceleration is available under any form for
+ * the view hierarchy.
+ *
+ * @return True if the view hierarchy can potentially be hardware accelerated,
+ * false otherwise
+ */
+ public static boolean isAvailable() {
+ return GLES20Canvas.isAvailable();
+ }
+
+ /**
* Destroys the hardware rendering context.
*/
abstract void destroy();
diff --git a/core/java/android/view/InputQueue.java b/core/java/android/view/InputQueue.java
index 13d8104..43c957a 100644
--- a/core/java/android/view/InputQueue.java
+++ b/core/java/android/view/InputQueue.java
@@ -132,9 +132,9 @@
synchronized (sLock) {
FinishedCallback callback = sRecycleHead;
if (callback != null) {
- callback.mRecycleNext = null;
sRecycleHead = callback.mRecycleNext;
sRecycleCount -= 1;
+ callback.mRecycleNext = null;
} else {
callback = new FinishedCallback();
}
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 9223e17..ed10e41 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -156,7 +156,7 @@
// those new codes. This is intended to maintain a consistent
// set of key code definitions across all Android devices.
- private static final int LAST_KEYCODE = KEYCODE_SWITCH_CHARSET;
+ private static final int LAST_KEYCODE = KEYCODE_BUTTON_MODE;
/**
* @deprecated There are now more than MAX_KEYCODE keycodes.
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index ff34f4a..0999598 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -312,7 +312,7 @@
* MotionEvent has no getRawX(int) method; simulate it pending future API approval.
*/
private static float getRawX(MotionEvent event, int pointerIndex) {
- float offset = event.getX() - event.getRawX();
+ float offset = event.getRawX() - event.getX();
return event.getX(pointerIndex) + offset;
}
@@ -320,7 +320,7 @@
* MotionEvent has no getRawY(int) method; simulate it pending future API approval.
*/
private static float getRawY(MotionEvent event, int pointerIndex) {
- float offset = event.getY() - event.getRawY();
+ float offset = event.getRawY() - event.getY();
return event.getY(pointerIndex) + offset;
}
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index 068e7b6..fb88c71 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -33,14 +33,15 @@
* and {@link #getXVelocity()}.
*/
public final class VelocityTracker implements Poolable<VelocityTracker> {
- static final String TAG = "VelocityTracker";
- static final boolean DEBUG = false;
- static final boolean localLOGV = DEBUG || Config.LOGV;
+ private static final String TAG = "VelocityTracker";
+ private static final boolean DEBUG = false;
+ private static final boolean localLOGV = DEBUG || Config.LOGV;
- static final int NUM_PAST = 10;
- static final int MAX_AGE_MILLISECONDS = 200;
+ private static final int NUM_PAST = 10;
+ private static final int MAX_AGE_MILLISECONDS = 200;
+
+ private static final int POINTER_POOL_CAPACITY = 20;
- static final VelocityTracker[] mPool = new VelocityTracker[1];
private static final Pool<VelocityTracker> sPool = Pools.synchronizedPool(
Pools.finitePool(new PoolableManager<VelocityTracker>() {
public VelocityTracker newInstance() {
@@ -48,16 +49,19 @@
}
public void onAcquired(VelocityTracker element) {
- element.clear();
}
public void onReleased(VelocityTracker element) {
+ element.clear();
}
}, 2));
- private static final int INITIAL_POINTERS = 5;
+ private static Pointer sRecycledPointerListHead;
+ private static int sRecycledPointerCount;
- private static final class PointerData {
+ private static final class Pointer {
+ public Pointer next;
+
public int id;
public float xVelocity;
public float yVelocity;
@@ -65,11 +69,13 @@
public final float[] pastX = new float[NUM_PAST];
public final float[] pastY = new float[NUM_PAST];
public final long[] pastTime = new long[NUM_PAST]; // uses Long.MIN_VALUE as a sentinel
+
+ public int generation;
}
- private PointerData[] mPointers = new PointerData[INITIAL_POINTERS];
- private int mNumPointers;
+ private Pointer mPointerListHead; // sorted by id in increasing order
private int mLastTouchIndex;
+ private int mGeneration;
private VelocityTracker mNext;
@@ -115,7 +121,9 @@
* Reset the velocity tracker back to its initial state.
*/
public void clear() {
- mNumPointers = 0;
+ releasePointerList(mPointerListHead);
+
+ mPointerListHead = null;
mLastTouchIndex = 0;
}
@@ -134,56 +142,62 @@
final int lastTouchIndex = mLastTouchIndex;
final int nextTouchIndex = (lastTouchIndex + 1) % NUM_PAST;
final int finalTouchIndex = (nextTouchIndex + historySize) % NUM_PAST;
+ final int generation = mGeneration++;
- if (pointerCount < mNumPointers) {
- final PointerData[] pointers = mPointers;
- int i = mNumPointers;
- while (--i >= 0) {
- final PointerData pointerData = pointers[i];
- if (ev.findPointerIndex(pointerData.id) == -1) {
- // Pointer went up.
- // Shuffle pointers down to fill the hole. Place the old pointer data at
- // the end so we can recycle it if more pointers are added later.
- mNumPointers -= 1;
- final int remaining = mNumPointers - i;
- if (remaining != 0) {
- System.arraycopy(pointers, i + 1, pointers, i, remaining);
- pointers[mNumPointers] = pointerData;
- }
- }
- }
- }
-
+ mLastTouchIndex = finalTouchIndex;
+
+ // Update pointer data.
+ Pointer previousPointer = null;
for (int i = 0; i < pointerCount; i++){
final int pointerId = ev.getPointerId(i);
- PointerData pointerData = getPointerData(pointerId);
- if (pointerData == null) {
- // Pointer went down.
- // Add a new entry. Write a sentinel at the end of the pastTime trace so we
- // will be able to tell where the trace started.
- final PointerData[] oldPointers = mPointers;
- final int newPointerIndex = mNumPointers;
- if (newPointerIndex < oldPointers.length) {
- pointerData = oldPointers[newPointerIndex];
- if (pointerData == null) {
- pointerData = new PointerData();
- oldPointers[newPointerIndex] = pointerData;
- }
- } else {
- final PointerData[] newPointers = new PointerData[newPointerIndex * 2];
- System.arraycopy(oldPointers, 0, newPointers, 0, newPointerIndex);
- mPointers = newPointers;
- pointerData = new PointerData();
- newPointers[newPointerIndex] = pointerData;
- }
- pointerData.id = pointerId;
- pointerData.pastTime[lastTouchIndex] = Long.MIN_VALUE;
- mNumPointers += 1;
+
+ // Find the pointer data for this pointer id.
+ // This loop is optimized for the common case where pointer ids in the event
+ // are in sorted order. However, we check for this case explicitly and
+ // perform a full linear scan from the start if needed.
+ Pointer nextPointer;
+ if (previousPointer == null || pointerId < previousPointer.id) {
+ previousPointer = null;
+ nextPointer = mPointerListHead;
+ } else {
+ nextPointer = previousPointer.next;
}
- final float[] pastX = pointerData.pastX;
- final float[] pastY = pointerData.pastY;
- final long[] pastTime = pointerData.pastTime;
+ final Pointer pointer;
+ for (;;) {
+ if (nextPointer != null) {
+ final int nextPointerId = nextPointer.id;
+ if (nextPointerId == pointerId) {
+ pointer = nextPointer;
+ break;
+ }
+ if (nextPointerId < pointerId) {
+ nextPointer = nextPointer.next;
+ continue;
+ }
+ }
+
+ // Pointer went down. Add it to the list.
+ // Write a sentinel at the end of the pastTime trace so we will be able to
+ // tell when the trace started.
+ pointer = obtainPointer();
+ pointer.id = pointerId;
+ pointer.pastTime[lastTouchIndex] = Long.MIN_VALUE;
+ pointer.next = nextPointer;
+ if (previousPointer == null) {
+ mPointerListHead = pointer;
+ } else {
+ previousPointer.next = pointer;
+ }
+ break;
+ }
+
+ pointer.generation = generation;
+ previousPointer = pointer;
+
+ final float[] pastX = pointer.pastX;
+ final float[] pastY = pointer.pastY;
+ final long[] pastTime = pointer.pastTime;
for (int j = 0; j < historySize; j++) {
final int touchIndex = (nextTouchIndex + j) % NUM_PAST;
@@ -196,7 +210,23 @@
pastTime[finalTouchIndex] = ev.getEventTime();
}
- mLastTouchIndex = finalTouchIndex;
+ // Find removed pointers.
+ previousPointer = null;
+ for (Pointer pointer = mPointerListHead; pointer != null; ) {
+ final Pointer nextPointer = pointer.next;
+ if (pointer.generation != generation) {
+ // Pointer went up. Remove it from the list.
+ if (previousPointer == null) {
+ mPointerListHead = nextPointer;
+ } else {
+ previousPointer.next = nextPointer;
+ }
+ releasePointer(pointer);
+ } else {
+ previousPointer = pointer;
+ }
+ pointer = nextPointer;
+ }
}
/**
@@ -223,13 +253,10 @@
* must be positive.
*/
public void computeCurrentVelocity(int units, float maxVelocity) {
- final int numPointers = mNumPointers;
- final PointerData[] pointers = mPointers;
final int lastTouchIndex = mLastTouchIndex;
- for (int p = 0; p < numPointers; p++) {
- final PointerData pointerData = pointers[p];
- final long[] pastTime = pointerData.pastTime;
+ for (Pointer pointer = mPointerListHead; pointer != null; pointer = pointer.next) {
+ final long[] pastTime = pointer.pastTime;
// Search backwards in time for oldest acceptable time.
// Stop at the beginning of the trace as indicated by the sentinel time Long.MIN_VALUE.
@@ -253,8 +280,8 @@
}
// Kind-of stupid.
- final float[] pastX = pointerData.pastX;
- final float[] pastY = pointerData.pastY;
+ final float[] pastX = pointer.pastX;
+ final float[] pastY = pointer.pastY;
final float oldestX = pastX[oldestTouchIndex];
final float oldestY = pastY[oldestTouchIndex];
@@ -290,11 +317,11 @@
accumY = maxVelocity;
}
- pointerData.xVelocity = accumX;
- pointerData.yVelocity = accumY;
+ pointer.xVelocity = accumX;
+ pointer.yVelocity = accumY;
if (localLOGV) {
- Log.v(TAG, "[" + p + "] Pointer " + pointerData.id
+ Log.v(TAG, "Pointer " + pointer.id
+ ": Y velocity=" + accumX +" X velocity=" + accumY + " N=" + numTouches);
}
}
@@ -307,8 +334,8 @@
* @return The previously computed X velocity.
*/
public float getXVelocity() {
- PointerData pointerData = getPointerData(0);
- return pointerData != null ? pointerData.xVelocity : 0;
+ Pointer pointer = getPointer(0);
+ return pointer != null ? pointer.xVelocity : 0;
}
/**
@@ -318,8 +345,8 @@
* @return The previously computed Y velocity.
*/
public float getYVelocity() {
- PointerData pointerData = getPointerData(0);
- return pointerData != null ? pointerData.yVelocity : 0;
+ Pointer pointer = getPointer(0);
+ return pointer != null ? pointer.yVelocity : 0;
}
/**
@@ -330,8 +357,8 @@
* @return The previously computed X velocity.
*/
public float getXVelocity(int id) {
- PointerData pointerData = getPointerData(id);
- return pointerData != null ? pointerData.xVelocity : 0;
+ Pointer pointer = getPointer(id);
+ return pointer != null ? pointer.xVelocity : 0;
}
/**
@@ -342,19 +369,68 @@
* @return The previously computed Y velocity.
*/
public float getYVelocity(int id) {
- PointerData pointerData = getPointerData(id);
- return pointerData != null ? pointerData.yVelocity : 0;
+ Pointer pointer = getPointer(id);
+ return pointer != null ? pointer.yVelocity : 0;
}
- private final PointerData getPointerData(int id) {
- final PointerData[] pointers = mPointers;
- final int numPointers = mNumPointers;
- for (int p = 0; p < numPointers; p++) {
- PointerData pointerData = pointers[p];
- if (pointerData.id == id) {
- return pointerData;
+ private final Pointer getPointer(int id) {
+ for (Pointer pointer = mPointerListHead; pointer != null; pointer = pointer.next) {
+ if (pointer.id == id) {
+ return pointer;
}
}
return null;
}
+
+ private static final Pointer obtainPointer() {
+ synchronized (sPool) {
+ if (sRecycledPointerCount != 0) {
+ Pointer element = sRecycledPointerListHead;
+ sRecycledPointerCount -= 1;
+ sRecycledPointerListHead = element.next;
+ element.next = null;
+ return element;
+ }
+ }
+ return new Pointer();
+ }
+
+ private static final void releasePointer(Pointer pointer) {
+ synchronized (sPool) {
+ if (sRecycledPointerCount < POINTER_POOL_CAPACITY) {
+ pointer.next = sRecycledPointerListHead;
+ sRecycledPointerCount += 1;
+ sRecycledPointerListHead = pointer;
+ }
+ }
+ }
+
+ private static final void releasePointerList(Pointer pointer) {
+ if (pointer != null) {
+ synchronized (sPool) {
+ int count = sRecycledPointerCount;
+ if (count >= POINTER_POOL_CAPACITY) {
+ return;
+ }
+
+ Pointer tail = pointer;
+ for (;;) {
+ count += 1;
+ if (count >= POINTER_POOL_CAPACITY) {
+ break;
+ }
+
+ Pointer next = tail.next;
+ if (next == null) {
+ break;
+ }
+ tail = next;
+ }
+
+ tail.next = sRecycledPointerListHead;
+ sRecycledPointerCount = count;
+ sRecycledPointerListHead = pointer;
+ }
+ }
+ }
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 735b35a..570793b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16,6 +16,7 @@
package android.view;
+import android.graphics.Camera;
import com.android.internal.R;
import com.android.internal.view.menu.MenuBuilder;
@@ -1374,14 +1375,14 @@
* Width as measured during measure pass.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "measurement")
protected int mMeasuredWidth;
/**
* Height as measured during measure pass.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "measurement")
protected int mMeasuredHeight;
/**
@@ -1436,8 +1437,8 @@
static final int MEASURED_DIMENSION_SET = 0x00000800;
/** {@hide} */
static final int FORCE_LAYOUT = 0x00001000;
-
- private static final int LAYOUT_REQUIRED = 0x00002000;
+ /** {@hide} */
+ static final int LAYOUT_REQUIRED = 0x00002000;
private static final int PRESSED = 0x00004000;
@@ -1537,6 +1538,14 @@
private static final int AWAKEN_SCROLL_BARS_ON_ATTACH = 0x08000000;
/**
+ * Indicates that pivotX or pivotY were explicitly set and we should not assume the center
+ * for transform operations
+ *
+ * @hide
+ */
+ private static final int PIVOT_EXPLICITLY_SET = 0x10000000;
+
+ /**
* The parent this view is attached to.
* {@hide}
*
@@ -1627,6 +1636,42 @@
private boolean mMatrixIsIdentity = true;
/**
+ * The Camera object is used to compute a 3D matrix when rotationX or rotationY are set.
+ */
+ private Camera mCamera = null;
+
+ /**
+ * This matrix is used when computing the matrix for 3D rotations.
+ */
+ private Matrix matrix3D = null;
+
+ /**
+ * These prev values are used to recalculate a centered pivot point when necessary. The
+ * pivot point is only used in matrix operations (when rotation, scale, or translation are
+ * set), so thes values are only used then as well.
+ */
+ private int mPrevWidth = -1;
+ private int mPrevHeight = -1;
+
+ /**
+ * Convenience value to check for float values that are close enough to zero to be considered
+ * zero.
+ */
+ private static float NONZERO_EPSILON = .001f;
+
+ /**
+ * The degrees rotation around the vertical axis through the pivot point.
+ */
+ @ViewDebug.ExportedProperty
+ private float mRotationY = 0f;
+
+ /**
+ * The degrees rotation around the horizontal axis through the pivot point.
+ */
+ @ViewDebug.ExportedProperty
+ private float mRotationX = 0f;
+
+ /**
* The degrees rotation around the pivot point.
*/
@ViewDebug.ExportedProperty
@@ -1684,28 +1729,28 @@
* to the left edge of this view.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
protected int mLeft;
/**
* The distance in pixels from the left edge of this view's parent
* to the right edge of this view.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
protected int mRight;
/**
* The distance in pixels from the top edge of this view's parent
* to the top edge of this view.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
protected int mTop;
/**
* The distance in pixels from the top edge of this view's parent
* to the bottom edge of this view.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
protected int mBottom;
/**
@@ -1713,14 +1758,14 @@
* horizontally.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "scrolling")
protected int mScrollX;
/**
* The offset, in pixels, by which the content of this view is scrolled
* vertically.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "scrolling")
protected int mScrollY;
/**
@@ -1728,28 +1773,28 @@
* left edge of this view and the left edge of its content.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "padding")
protected int mPaddingLeft;
/**
* The right padding in pixels, that is the distance in pixels between the
* right edge of this view and the right edge of its content.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "padding")
protected int mPaddingRight;
/**
* The top padding in pixels, that is the distance in pixels between the
* top edge of this view and the top edge of its content.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "padding")
protected int mPaddingTop;
/**
* The bottom padding in pixels, that is the distance in pixels between the
* bottom edge of this view and the bottom edge of its content.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "padding")
protected int mPaddingBottom;
/**
@@ -1760,13 +1805,13 @@
/**
* Cache the paddingRight set by the user to append to the scrollbar's size.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "padding")
int mUserPaddingRight;
/**
* Cache the paddingBottom set by the user to append to the scrollbar's size.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "padding")
int mUserPaddingBottom;
/**
@@ -1873,14 +1918,14 @@
* The minimum height of the view. We'll try our best to have the height
* of this view to at least this amount.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "measurement")
private int mMinHeight;
/**
* The minimum width of the view. We'll try our best to have the width
* of this view to at least this amount.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "measurement")
private int mMinWidth;
/**
@@ -2722,7 +2767,7 @@
*
* @return True if this view has or contains focus, false otherwise.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "focus")
public boolean hasFocus() {
return (mPrivateFlags & FOCUSED) != 0;
}
@@ -2900,7 +2945,7 @@
*
* @return True if this view has focus, false otherwise.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "focus")
public boolean isFocused() {
return (mPrivateFlags & FOCUSED) != 0;
}
@@ -3311,7 +3356,7 @@
*
* @return true if this view has nothing to draw, false otherwise
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "drawing")
public boolean willNotDraw() {
return (mViewFlags & DRAW_MASK) == WILL_NOT_DRAW;
}
@@ -3334,7 +3379,7 @@
*
* @return true if this view does not cache its drawing, false otherwise
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "drawing")
public boolean willNotCacheDrawing() {
return (mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING;
}
@@ -3509,7 +3554,7 @@
* @return True if this view can take focus, or false otherwise.
* @attr ref android.R.styleable#View_focusable
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "focus")
public final boolean isFocusable() {
return FOCUSABLE == (mViewFlags & FOCUSABLE_MASK);
}
@@ -4820,7 +4865,7 @@
*
* @return The width of your view, in pixels.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public final int getWidth() {
return mRight - mLeft;
}
@@ -4830,7 +4875,7 @@
*
* @return The height of your view, in pixels.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public final int getHeight() {
return mBottom - mTop;
}
@@ -4888,6 +4933,16 @@
}
/**
+ * Utility function to determine if the value is far enough away from zero to be
+ * considered non-zero.
+ * @param value A floating point value to check for zero-ness
+ * @return whether the passed-in value is far enough away from zero to be considered non-zero
+ */
+ private static boolean nonzero(float value) {
+ return (value < -NONZERO_EPSILON || value > NONZERO_EPSILON);
+ }
+
+ /**
* Recomputes the transform matrix if necessary.
*
* @return True if the transform matrix is the identity matrix, false otherwise.
@@ -4896,10 +4951,34 @@
if (mMatrixDirty) {
// transform-related properties have changed since the last time someone
// asked for the matrix; recalculate it with the current values
+
+ // Figure out if we need to update the pivot point
+ if ((mPrivateFlags & PIVOT_EXPLICITLY_SET) == 0) {
+ if ((mRight - mLeft) != mPrevWidth && (mBottom - mTop) != mPrevHeight) {
+ mPrevWidth = mRight - mLeft;
+ mPrevHeight = mBottom - mTop;
+ mPivotX = (float) mPrevWidth / 2f;
+ mPivotY = (float) mPrevHeight / 2f;
+ }
+ }
mMatrix.reset();
mMatrix.setTranslate(mTranslationX, mTranslationY);
mMatrix.preRotate(mRotation, mPivotX, mPivotY);
mMatrix.preScale(mScaleX, mScaleY, mPivotX, mPivotY);
+ if (nonzero(mRotationX) || nonzero(mRotationY)) {
+ if (mCamera == null) {
+ mCamera = new Camera();
+ matrix3D = new Matrix();
+ }
+ mCamera.save();
+ mCamera.rotateX(mRotationX);
+ mCamera.rotateY(mRotationY);
+ mCamera.getMatrix(matrix3D);
+ matrix3D.preTranslate(-mPivotX, -mPivotY);
+ matrix3D.postTranslate(mPivotX, mPivotY);
+ mMatrix.postConcat(matrix3D);
+ mCamera.restore();
+ }
mMatrixDirty = false;
mMatrixIsIdentity = mMatrix.isIdentity();
mInverseMatrixDirty = true;
@@ -4955,6 +5034,64 @@
}
/**
+ * The degrees that the view is rotated around the vertical axis through the pivot point.
+ *
+ * @see #getPivotX()
+ * @see #getPivotY()
+ * @return The degrees of Y rotation.
+ */
+ public float getRotationY() {
+ return mRotationY;
+ }
+
+ /**
+ * Sets the degrees that the view is rotated around the vertical axis through pivot point.
+ *
+ * @param rotationY The degrees of Y rotation.
+ * @see #getPivotX()
+ * @see #getPivotY()
+ */
+ public void setRotationY(float rotationY) {
+ if (mRotationY != rotationY) {
+ // Double-invalidation is necessary to capture view's old and new areas
+ invalidate();
+ mRotationY = rotationY;
+ mMatrixDirty = true;
+ mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+ invalidate();
+ }
+ }
+
+ /**
+ * The degrees that the view is rotated around the horizontal axis through the pivot point.
+ *
+ * @see #getPivotX()
+ * @see #getPivotY()
+ * @return The degrees of X rotation.
+ */
+ public float getRotationX() {
+ return mRotationX;
+ }
+
+ /**
+ * Sets the degrees that the view is rotated around the horizontal axis through pivot point.
+ *
+ * @param rotationX The degrees of X rotation.
+ * @see #getPivotX()
+ * @see #getPivotY()
+ */
+ public void setRotationX(float rotationX) {
+ if (mRotationX != rotationX) {
+ // Double-invalidation is necessary to capture view's old and new areas
+ invalidate();
+ mRotationX = rotationX;
+ mMatrixDirty = true;
+ mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+ invalidate();
+ }
+ }
+
+ /**
* The amount that the view is scaled in x around the pivot point, as a proportion of
* the view's unscaled width. A value of 1, the default, means that no scaling is applied.
*
@@ -5035,6 +5172,9 @@
/**
* Sets the x location of the point around which the view is
* {@link #setRotation(float) rotated} and {@link #setScaleX(float) scaled}.
+ * By default, the pivot point is centered on the object.
+ * Setting this property disables this behavior and causes the view to use only the
+ * explicitly set pivotX and pivotY values.
*
* @param pivotX The x location of the pivot point.
* @see #getRotation()
@@ -5043,6 +5183,7 @@
* @see #getPivotY()
*/
public void setPivotX(float pivotX) {
+ mPrivateFlags |= PIVOT_EXPLICITLY_SET;
if (mPivotX != pivotX) {
// Double-invalidation is necessary to capture view's old and new areas
invalidate();
@@ -5069,7 +5210,9 @@
/**
* Sets the y location of the point around which the view is {@link #setRotation(float) rotated}
- * and {@link #setScaleY(float) scaled}.
+ * and {@link #setScaleY(float) scaled}. By default, the pivot point is centered on the object.
+ * Setting this property disables this behavior and causes the view to use only the
+ * explicitly set pivotX and pivotY values.
*
* @param pivotY The y location of the pivot point.
* @see #getRotation()
@@ -5078,6 +5221,7 @@
* @see #getPivotY()
*/
public void setPivotY(float pivotY) {
+ mPrivateFlags |= PIVOT_EXPLICITLY_SET;
if (mPivotY != pivotY) {
// Double-invalidation is necessary to capture view's old and new areas
invalidate();
@@ -5312,15 +5456,6 @@
* is still within the view.
*/
private boolean pointInView(float localX, float localY, float slop) {
- if (!hasIdentityMatrix() && mAttachInfo != null) {
- // non-identity matrix: transform the point into the view's coordinates
- final float[] localXY = mAttachInfo.mTmpTransformLocation;
- localXY[0] = localX;
- localXY[1] = localY;
- getInverseMatrix().mapPoints(localXY);
- localX = localXY[0];
- localY = localXY[1];
- }
return localX > -slop && localY > -slop && localX < ((mRight - mLeft) + slop) &&
localY < ((mBottom - mTop) + slop);
}
@@ -5781,7 +5916,7 @@
*
* @return True if this View is guaranteed to be fully opaque, false otherwise.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "drawing")
public boolean isOpaque() {
return (mPrivateFlags & OPAQUE_MASK) == OPAQUE_MASK;
}
@@ -6866,7 +7001,7 @@
* @see #setDrawingCacheEnabled(boolean)
* @see #getDrawingCache()
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "drawing")
public boolean isDrawingCacheEnabled() {
return (mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED;
}
@@ -8730,7 +8865,7 @@
* @return the offset of the baseline within the widget's bounds or -1
* if baseline alignment is not supported
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public int getBaseline() {
return -1;
}
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 5dd45f9..2ca08ea 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -255,6 +255,14 @@
* @see #deepExport()
*/
String prefix() default "";
+
+ /**
+ * Specifies the category the property falls into, such as measurement,
+ * layout, drawing, etc.
+ *
+ * @return the category as String
+ */
+ String category() default "";
}
/**
@@ -934,65 +942,76 @@
private static void profileViewAndChildren(final View view, BufferedWriter out)
throws IOException {
- final long durationMeasure = profileViewOperation(view, new ViewOperation<Void>() {
- public Void[] pre() {
- forceLayout(view);
- return null;
- }
+ profileViewAndChildren(view, out, true);
+ }
- private void forceLayout(View view) {
- view.forceLayout();
- if (view instanceof ViewGroup) {
- ViewGroup group = (ViewGroup) view;
- final int count = group.getChildCount();
- for (int i = 0; i < count; i++) {
- forceLayout(group.getChildAt(i));
- }
- }
- }
+ private static void profileViewAndChildren(final View view, BufferedWriter out, boolean root)
+ throws IOException {
- public void run(Void... data) {
- view.measure(view.mOldWidthMeasureSpec, view.mOldHeightMeasureSpec);
- }
+ long durationMeasure =
+ (root || (view.mPrivateFlags & View.MEASURED_DIMENSION_SET) != 0) ? profileViewOperation(
+ view, new ViewOperation<Void>() {
+ public Void[] pre() {
+ forceLayout(view);
+ return null;
+ }
- public void post(Void... data) {
- }
- });
+ private void forceLayout(View view) {
+ view.forceLayout();
+ if (view instanceof ViewGroup) {
+ ViewGroup group = (ViewGroup) view;
+ final int count = group.getChildCount();
+ for (int i = 0; i < count; i++) {
+ forceLayout(group.getChildAt(i));
+ }
+ }
+ }
- final long durationLayout = profileViewOperation(view, new ViewOperation<Void>() {
- public Void[] pre() {
- return null;
- }
+ public void run(Void... data) {
+ view.measure(view.mOldWidthMeasureSpec, view.mOldHeightMeasureSpec);
+ }
- public void run(Void... data) {
- view.layout(view.mLeft, view.mTop, view.mRight, view.mBottom);
- }
+ public void post(Void... data) {
+ }
+ })
+ : 0;
+ long durationLayout =
+ (root || (view.mPrivateFlags & View.LAYOUT_REQUIRED) != 0) ? profileViewOperation(
+ view, new ViewOperation<Void>() {
+ public Void[] pre() {
+ return null;
+ }
- public void post(Void... data) {
- }
- });
+ public void run(Void... data) {
+ view.layout(view.mLeft, view.mTop, view.mRight, view.mBottom);
+ }
- final long durationDraw = profileViewOperation(view, new ViewOperation<Object>() {
- public Object[] pre() {
- final DisplayMetrics metrics = view.getResources().getDisplayMetrics();
- final Bitmap bitmap =
- Bitmap.createBitmap(metrics.widthPixels, metrics.heightPixels,
- Bitmap.Config.RGB_565);
- final Canvas canvas = new Canvas(bitmap);
- return new Object[] {
- bitmap, canvas
- };
- }
+ public void post(Void... data) {
+ }
+ }) : 0;
+ long durationDraw =
+ (root || (view.mPrivateFlags & View.DRAWN) != 0) ? profileViewOperation(view,
+ new ViewOperation<Object>() {
+ public Object[] pre() {
+ final DisplayMetrics metrics =
+ view.getResources().getDisplayMetrics();
+ final Bitmap bitmap =
+ Bitmap.createBitmap(metrics.widthPixels,
+ metrics.heightPixels, Bitmap.Config.RGB_565);
+ final Canvas canvas = new Canvas(bitmap);
+ return new Object[] {
+ bitmap, canvas
+ };
+ }
- public void run(Object... data) {
- view.draw((Canvas) data[1]);
- }
+ public void run(Object... data) {
+ view.draw((Canvas) data[1]);
+ }
- public void post(Object... data) {
- ((Bitmap) data[0]).recycle();
- }
- });
-
+ public void post(Object... data) {
+ ((Bitmap) data[0]).recycle();
+ }
+ }) : 0;
out.write(String.valueOf(durationMeasure));
out.write(' ');
out.write(String.valueOf(durationLayout));
@@ -1003,7 +1022,7 @@
ViewGroup group = (ViewGroup) view;
final int count = group.getChildCount();
for (int i = 0; i < count; i++) {
- profileViewAndChildren(group.getChildAt(i), out);
+ profileViewAndChildren(group.getChildAt(i), out, false);
}
}
}
@@ -1033,7 +1052,10 @@
});
try {
- latch.await(CAPTURE_TIMEOUT, TimeUnit.MILLISECONDS);
+ if (!latch.await(CAPTURE_TIMEOUT, TimeUnit.MILLISECONDS)) {
+ Log.w("View", "Could not complete the profiling of the view " + view);
+ return -1;
+ }
} catch (InterruptedException e) {
Log.w("View", "Could not complete the profiling of the view " + view);
Thread.currentThread().interrupt();
@@ -1354,9 +1376,12 @@
// TODO: This should happen on the UI thread
Object methodValue = method.invoke(view, (Object[]) null);
final Class<?> returnType = method.getReturnType();
+ final ExportedProperty property = sAnnotations.get(method);
+ String categoryPrefix =
+ property.category().length() != 0 ? property.category() + ":" : "";
if (returnType == int.class) {
- final ExportedProperty property = sAnnotations.get(method);
+
if (property.resolveId() && context != null) {
final int id = (Integer) methodValue;
methodValue = resolveId(context, id);
@@ -1364,7 +1389,8 @@
final FlagToString[] flagsMapping = property.flagMapping();
if (flagsMapping.length > 0) {
final int intValue = (Integer) methodValue;
- final String valuePrefix = prefix + method.getName() + '_';
+ final String valuePrefix =
+ categoryPrefix + prefix + method.getName() + '_';
exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
}
@@ -1388,21 +1414,22 @@
}
}
} else if (returnType == int[].class) {
- final ExportedProperty property = sAnnotations.get(method);
final int[] array = (int[]) methodValue;
- final String valuePrefix = prefix + method.getName() + '_';
+ final String valuePrefix = categoryPrefix + prefix + method.getName() + '_';
final String suffix = "()";
exportUnrolledArray(context, out, property, array, valuePrefix, suffix);
+
+ // Probably want to return here, same as for fields.
+ return;
} else if (!returnType.isPrimitive()) {
- final ExportedProperty property = sAnnotations.get(method);
if (property.deepExport()) {
dumpViewProperties(context, methodValue, out, prefix + property.prefix());
continue;
}
}
- writeEntry(out, prefix, method.getName(), "()", methodValue);
+ writeEntry(out, categoryPrefix + prefix, method.getName(), "()", methodValue);
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
}
@@ -1422,9 +1449,12 @@
try {
Object fieldValue = null;
final Class<?> type = field.getType();
+ final ExportedProperty property = sAnnotations.get(field);
+ String categoryPrefix =
+ property.category().length() != 0 ? property.category() + ":" : "";
if (type == int.class) {
- final ExportedProperty property = sAnnotations.get(field);
+
if (property.resolveId() && context != null) {
final int id = field.getInt(view);
fieldValue = resolveId(context, id);
@@ -1432,7 +1462,8 @@
final FlagToString[] flagsMapping = property.flagMapping();
if (flagsMapping.length > 0) {
final int intValue = field.getInt(view);
- final String valuePrefix = prefix + field.getName() + '_';
+ final String valuePrefix =
+ categoryPrefix + prefix + field.getName() + '_';
exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
}
@@ -1454,9 +1485,8 @@
}
}
} else if (type == int[].class) {
- final ExportedProperty property = sAnnotations.get(field);
final int[] array = (int[]) field.get(view);
- final String valuePrefix = prefix + field.getName() + '_';
+ final String valuePrefix = categoryPrefix + prefix + field.getName() + '_';
final String suffix = "";
exportUnrolledArray(context, out, property, array, valuePrefix, suffix);
@@ -1464,10 +1494,9 @@
// We exit here!
return;
} else if (!type.isPrimitive()) {
- final ExportedProperty property = sAnnotations.get(field);
if (property.deepExport()) {
- dumpViewProperties(context, field.get(view), out,
- prefix + property.prefix());
+ dumpViewProperties(context, field.get(view), out, prefix
+ + property.prefix());
continue;
}
}
@@ -1476,7 +1505,7 @@
fieldValue = field.get(view);
}
- writeEntry(out, prefix, field.getName(), "", fieldValue);
+ writeEntry(out, categoryPrefix + prefix, field.getName(), "", fieldValue);
} catch (IllegalAccessException e) {
}
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 9da5637..e2f9c15 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -377,7 +377,7 @@
* @return one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
* {@link #FOCUS_BLOCK_DESCENDANTS}.
*/
- @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.ExportedProperty(category = "focus", mapping = {
@ViewDebug.IntToString(from = FOCUS_BEFORE_DESCENDANTS, to = "FOCUS_BEFORE_DESCENDANTS"),
@ViewDebug.IntToString(from = FOCUS_AFTER_DESCENDANTS, to = "FOCUS_AFTER_DESCENDANTS"),
@ViewDebug.IntToString(from = FOCUS_BLOCK_DESCENDANTS, to = "FOCUS_BLOCK_DESCENDANTS")
@@ -905,19 +905,16 @@
}
// Calculate the offset point into the target's local coordinates
- float xc;
- float yc;
- if (target.hasIdentityMatrix() || mAttachInfo == null) {
- xc = scrolledXFloat - (float) target.mLeft;
- yc = scrolledYFloat - (float) target.mTop;
- } else {
+ float xc = scrolledXFloat - (float) target.mLeft;
+ float yc = scrolledYFloat - (float) target.mTop;
+ if (!target.hasIdentityMatrix() && mAttachInfo != null) {
// non-identity matrix: transform the point into the view's coordinates
final float[] localXY = mAttachInfo.mTmpTransformLocation;
- localXY[0] = scrolledXFloat;
- localXY[1] = scrolledYFloat;
+ localXY[0] = xc;
+ localXY[1] = yc;
target.getInverseMatrix().mapPoints(localXY);
- xc = localXY[0] - (float) target.mLeft;
- yc = localXY[1] - (float) target.mTop;
+ xc = localXY[0];
+ yc = localXY[1];
}
// if have a target, see if we're allowed to and want to intercept its
@@ -2835,7 +2832,7 @@
* @see #setChildrenDrawnWithCacheEnabled(boolean)
* @see View#setDrawingCacheEnabled(boolean)
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "drawing")
public boolean isAlwaysDrawnWithCacheEnabled() {
return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE;
}
@@ -2870,7 +2867,7 @@
* @see #setAlwaysDrawnWithCacheEnabled(boolean)
* @see #setChildrenDrawnWithCacheEnabled(boolean)
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "drawing")
protected boolean isChildrenDrawnWithCacheEnabled() {
return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE;
}
@@ -2902,7 +2899,7 @@
* @see #setChildrenDrawingOrderEnabled(boolean)
* @see #getChildDrawingOrder(int, int)
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "drawing")
protected boolean isChildrenDrawingOrderEnabled() {
return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER;
}
@@ -2939,7 +2936,7 @@
* {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
* and {@link #PERSISTENT_ALL_CACHES}
*/
- @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.ExportedProperty(category = "drawing", mapping = {
@ViewDebug.IntToString(from = PERSISTENT_NO_CACHE, to = "NONE"),
@ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"),
@ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"),
@@ -3572,7 +3569,7 @@
* constants FILL_PARENT (replaced by MATCH_PARENT ,
* in API Level 8) or WRAP_CONTENT. or an exact size.
*/
- @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.ExportedProperty(category = "layout", mapping = {
@ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
@ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
})
@@ -3583,7 +3580,7 @@
* constants FILL_PARENT (replaced by MATCH_PARENT ,
* in API Level 8) or WRAP_CONTENT. or an exact size.
*/
- @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.ExportedProperty(category = "layout", mapping = {
@ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
@ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
})
@@ -3708,25 +3705,25 @@
/**
* The left margin in pixels of the child.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public int leftMargin;
/**
* The top margin in pixels of the child.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public int topMargin;
/**
* The right margin in pixels of the child.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public int rightMargin;
/**
* The bottom margin in pixels of the child.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public int bottomMargin;
/**
diff --git a/core/java/android/webkit/DeviceOrientationManager.java b/core/java/android/webkit/DeviceOrientationManager.java
new file mode 100644
index 0000000..778b043
--- /dev/null
+++ b/core/java/android/webkit/DeviceOrientationManager.java
@@ -0,0 +1,55 @@
+/*
+ * 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.webkit;
+
+import android.util.Log;
+
+/**
+ * This class is simply a container for the methods used to configure WebKit's
+ * mock DeviceOrientationClient for use in LayoutTests.
+ *
+ * This could be part of WebViewCore, but have moved it to its own class to
+ * avoid bloat there.
+ * @hide
+ */
+public final class DeviceOrientationManager {
+ /**
+ * Sets whether the Page for the specified WebViewCore should use a mock DeviceOrientation
+ * client.
+ */
+ public static void useMock(WebViewCore webViewCore) {
+ assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
+ nativeUseMock(webViewCore);
+ }
+
+ /**
+ * Set the position for the mock DeviceOrientation service for the supplied WebViewCore.
+ */
+ public static void setMockOrientation(WebViewCore webViewCore, boolean canProvideAlpha,
+ double alpha, boolean canProvideBeta, double beta, boolean canProvideGamma,
+ double gamma) {
+ assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
+ nativeSetMockOrientation(webViewCore, canProvideAlpha, alpha, canProvideBeta, beta,
+ canProvideGamma, gamma);
+ }
+
+ // Native functions
+ private static native void nativeUseMock(WebViewCore webViewCore);
+ private static native void nativeSetMockOrientation(WebViewCore webViewCore,
+ boolean canProvideAlpha, double alpha, boolean canProvideBeta, double beta,
+ boolean canProvideGamma, double gamma);
+}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index d1b0902..44f036b 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -66,6 +66,7 @@
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
+import android.webkit.DeviceOrientationManager;
import android.webkit.WebTextView.AutoCompleteAdapter;
import android.webkit.WebViewCore.EventHub;
import android.webkit.WebViewCore.TouchEventData;
@@ -3747,6 +3748,26 @@
}
/**
+ * Called by DRT on UI thread, need to proxy to WebCore thread.
+ *
+ * @hide debug only
+ */
+ public void useMockDeviceOrientation() {
+ mWebViewCore.sendMessage(EventHub.USE_MOCK_DEVICE_ORIENTATION);
+ }
+
+ /**
+ * Called by DRT on WebCore thread.
+ *
+ * @hide debug only
+ */
+ public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+ boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+ DeviceOrientationManager.setMockOrientation(mWebViewCore, canProvideAlpha, alpha,
+ canProvideBeta, beta, canProvideGamma, gamma);
+ }
+
+ /**
* Dump the V8 counters to standard output.
* Note that you need a build with V8 and WEBCORE_INSTRUMENTATION set to
* true. Otherwise, this will do nothing.
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 3c28c94..9ec97cd 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -33,6 +33,7 @@
import android.view.KeyEvent;
import android.view.SurfaceView;
import android.view.View;
+import android.webkit.DeviceOrientationManager;
import java.util.ArrayList;
import java.util.Collection;
@@ -878,6 +879,8 @@
// accessibility support
static final int MODIFY_SELECTION = 190;
+ static final int USE_MOCK_DEVICE_ORIENTATION = 191;
+
// private message ids
private static final int DESTROY = 200;
@@ -1409,6 +1412,10 @@
WebView.SET_TOUCH_HIGHLIGHT_RECTS, null)
.sendToTarget();
break;
+
+ case USE_MOCK_DEVICE_ORIENTATION:
+ useMockDeviceOrientation();
+ break;
}
}
};
@@ -2481,6 +2488,10 @@
hMode, vMode).sendToTarget();
}
+ private void useMockDeviceOrientation() {
+ DeviceOrientationManager.useMock(this);
+ }
+
private native void nativePause();
private native void nativeResume();
private native void nativeFreeMemory();
diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java
index d75d421..d7b4452 100644
--- a/core/java/android/webkit/WebViewDatabase.java
+++ b/core/java/android/webkit/WebViewDatabase.java
@@ -772,6 +772,9 @@
}
long getCacheTotalSize() {
+ if (mCacheDatabase == null) {
+ return 0;
+ }
long size = 0;
Cursor cursor = null;
final String query = "SELECT SUM(contentlength) as sum FROM cache";
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 372cc83..e572d3d 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3998,7 +3998,7 @@
* View type for this view, as returned by
* {@link android.widget.Adapter#getItemViewType(int) }
*/
- @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.ExportedProperty(category = "list", mapping = {
@ViewDebug.IntToString(from = ITEM_VIEW_TYPE_IGNORE, to = "ITEM_VIEW_TYPE_IGNORE"),
@ViewDebug.IntToString(from = ITEM_VIEW_TYPE_HEADER_OR_FOOTER, to = "ITEM_VIEW_TYPE_HEADER_OR_FOOTER")
})
@@ -4010,7 +4010,7 @@
* been added to the list view and whether they should be treated as
* recycled views or not.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "list")
boolean recycledHeaderFooter;
/**
@@ -4021,7 +4021,7 @@
* view to be attached to the window rather than just attached to the
* parent.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "list")
boolean forceAdd;
public LayoutParams(Context c, AttributeSet attrs) {
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index fe6d91a..10a8729 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -56,7 +56,7 @@
/**
* The position of the first child displayed
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "scrolling")
int mFirstPosition = 0;
/**
@@ -141,7 +141,7 @@
* The position within the adapter's data set of the item to select
* during the next layout.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "list")
int mNextSelectedPosition = INVALID_POSITION;
/**
@@ -152,7 +152,7 @@
/**
* The position within the adapter's data set of the currently selected item.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "list")
int mSelectedPosition = INVALID_POSITION;
/**
@@ -168,7 +168,7 @@
/**
* The number of items in the current adapter.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "list")
int mItemCount;
/**
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index a6d51702..2b723c9 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -16,14 +16,22 @@
package android.widget;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+
+import android.animation.PropertyAnimator;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
+import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
@@ -35,23 +43,100 @@
* @attr ref android.R.styleable#AdapterViewAnimator_outAnimation
* @attr ref android.R.styleable#AdapterViewAnimator_animateFirstView
*/
-public class AdapterViewAnimator extends AdapterView<Adapter> implements RemoteViewsAdapter.RemoteAdapterConnectionCallback{
+public abstract class AdapterViewAnimator extends AdapterView<Adapter>
+ implements RemoteViewsAdapter.RemoteAdapterConnectionCallback{
private static final String TAG = "RemoteViewAnimator";
+ /**
+ * The index of the current child, which appears anywhere from the beginning
+ * to the end of the current set of children, as specified by {@link #mActiveOffset}
+ */
int mWhichChild = 0;
- boolean mFirstTime = true;
+
+ /**
+ * Whether or not the first view(s) should be animated in
+ */
boolean mAnimateFirstTime = true;
+ /**
+ * Represents where the in the current window of
+ * views the current <code>mDisplayedChild</code> sits
+ */
+ int mActiveOffset = 0;
+
+ /**
+ * The number of views that the {@link AdapterViewAnimator} keeps as children at any
+ * given time (not counting views that are pending removal, see {@link #mPreviousViews}).
+ */
+ int mNumActiveViews = 1;
+
+ /**
+ * Array of the children of the {@link AdapterViewAnimator}. This array
+ * is accessed in a circular fashion
+ */
+ View[] mActiveViews;
+
+ /**
+ * List of views pending removal from the {@link AdapterViewAnimator}
+ */
+ ArrayList<View> mPreviousViews;
+
+ /**
+ * The index, relative to the adapter, of the beginning of the window of views
+ */
+ int mCurrentWindowStart = 0;
+
+ /**
+ * The index, relative to the adapter, of the end of the window of views
+ */
+ int mCurrentWindowEnd = -1;
+
+ /**
+ * The same as {@link #mCurrentWindowStart}, except when the we have bounded
+ * {@link #mCurrentWindowStart} to be non-negative
+ */
+ int mCurrentWindowStartUnbounded = 0;
+
+ /**
+ * Indicates whether to treat the adapter to be a circular structure, ie.
+ * the view before 0 is considered to be <code>mAdapter.getCount() - 1</code>
+ *
+ * TODO: this doesn't do anything yet
+ *
+ */
+ boolean mCycleViews = false;
+
+ /**
+ * Handler to post events to the main thread
+ */
+ Handler mMainQueue;
+
+ /**
+ * Listens for data changes from the adapter
+ */
AdapterDataSetObserver mDataSetObserver;
- View mPreviousView;
- View mCurrentView;
+ /**
+ * The {@link Adapter} for this {@link AdapterViewAnimator}
+ */
+ Adapter mAdapter;
+ /**
+ * The {@link RemoteViewsAdapter} for this {@link AdapterViewAnimator}
+ */
+ RemoteViewsAdapter mRemoteViewsAdapter;
+
+ /**
+ * Specifies whether this is the first time the animator is showing views
+ */
+ boolean mFirstTime = true;
+
+ /**
+ * TODO: Animation stuff is still in flux, waiting on the new framework to settle a bit.
+ */
Animation mInAnimation;
Animation mOutAnimation;
- Adapter mAdapter;
- RemoteViewsAdapter mRemoteViewsAdapter;
- private Handler mMainQueue;
+ private ArrayList<View> mViewsToBringToFront;
public AdapterViewAnimator(Context context) {
super(context);
@@ -61,8 +146,10 @@
public AdapterViewAnimator(Context context, AttributeSet attrs) {
super(context, attrs);
- TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.ViewAnimator);
- int resource = a.getResourceId(com.android.internal.R.styleable.ViewAnimator_inAnimation, 0);
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.ViewAnimator);
+ int resource = a.getResourceId(
+ com.android.internal.R.styleable.ViewAnimator_inAnimation, 0);
if (resource > 0) {
setInAnimation(context, resource);
}
@@ -72,7 +159,8 @@
setOutAnimation(context, resource);
}
- boolean flag = a.getBoolean(com.android.internal.R.styleable.ViewAnimator_animateFirstView, true);
+ boolean flag = a.getBoolean(
+ com.android.internal.R.styleable.ViewAnimator_animateFirstView, true);
setAnimateFirstView(flag);
a.recycle();
@@ -85,6 +173,54 @@
*/
private void initViewAnimator(Context context, AttributeSet attrs) {
mMainQueue = new Handler(Looper.myLooper());
+ mActiveViews = new View[mNumActiveViews];
+ mPreviousViews = new ArrayList<View>();
+ mViewsToBringToFront = new ArrayList<View>();
+ }
+
+ /**
+ * This method is used by subclasses to configure the animator to display the
+ * desired number of views, and specify the offset
+ *
+ * @param numVisibleViews The number of views the animator keeps in the {@link ViewGroup}
+ * @param activeOffset This parameter specifies where the current index ({@link mWhichChild})
+ * sits within the window. For example if activeOffset is 1, and numVisibleViews is 3,
+ * and {@link setDisplayedChild} is called with 10, then the effective window will be
+ * the indexes 9, 10, and 11. In the same example, if activeOffset were 0, then the
+ * window would instead contain indexes 10, 11 and 12.
+ */
+ void configureViewAnimator(int numVisibleViews, int activeOffset) {
+ if (activeOffset > numVisibleViews - 1) {
+ // Throw an exception here.
+ }
+ mNumActiveViews = numVisibleViews;
+ mActiveOffset = activeOffset;
+ mActiveViews = new View[mNumActiveViews];
+ mPreviousViews.clear();
+ removeAllViewsInLayout();
+ mCurrentWindowStart = 0;
+ mCurrentWindowEnd = -1;
+ }
+
+ /**
+ * This class should be overridden by subclasses to customize view transitions within
+ * the set of visible views
+ *
+ * @param fromIndex The relative index within the window that the view was in, -1 if it wasn't
+ * in the window
+ * @param toIndex The relative index within the window that the view is going to, -1 if it is
+ * being removed
+ * @param view The view that is being animated
+ */
+ void animateViewForTransition(int fromIndex, int toIndex, View view) {
+ PropertyAnimator pa;
+ if (fromIndex == -1) {
+ pa = new PropertyAnimator(400, view, "alpha", 0.0f, 1.0f);
+ pa.start();
+ } else if (toIndex == -1) {
+ pa = new PropertyAnimator(400, view, "alpha", 1.0f, 0.0f);
+ pa.start();
+ }
}
/**
@@ -114,18 +250,28 @@
/**
* Return default inAnimation. To be overriden by subclasses.
*/
- public Animation getDefaultInAnimation() {
+ Animation getDefaultInAnimation() {
return null;
}
/**
- * Return default outAnimation. To be overriden by subclasses.
+ * Return default outAnimation. To be overridden by subclasses.
*/
- public Animation getDefaultOutAnimation() {
+ Animation getDefaultOutAnimation() {
return null;
}
/**
+ * To be overridden by subclasses. This method applies a view / index specific
+ * transform to the child view.
+ *
+ * @param child
+ * @param relativeIndex
+ */
+ void applyTransformForChildAtIndex(View child, int relativeIndex) {
+ }
+
+ /**
* Returns the index of the currently displayed child view.
*/
public int getDisplayedChild() {
@@ -160,70 +306,137 @@
showOnly(childIndex, animate, false);
}
- private LayoutParams makeLayoutParams() {
- int width = mMeasuredWidth - mPaddingLeft - mPaddingRight;
- int height = mMeasuredHeight - mPaddingTop - mPaddingBottom;
- return new LayoutParams(width, height);
+ private int modulo(int pos, int size) {
+ return (size + (pos % size)) % size;
}
- protected void showOnly(int childIndex, boolean animate, boolean onLayout) {
- if (mAdapter != null) {
- // The previous view should be removed from the ViewGroup
- if (mPreviousView != null) {
- mPreviousView.clearAnimation();
+ /**
+ * Get the view at this index relative to the current window's start
+ *
+ * @param relativeIndex Position relative to the current window's start
+ * @return View at this index, null if the index is outside the bounds
+ */
+ View getViewAtRelativeIndex(int relativeIndex) {
+ if (relativeIndex >= 0 && relativeIndex <= mNumActiveViews - 1) {
+ int index = mCurrentWindowStartUnbounded + relativeIndex;
+ return mActiveViews[modulo(index, mNumActiveViews)];
+ }
+ return null;
+ }
- // TODO: this is where we would store the the view for
- // recycling
- removeViewInLayout(mPreviousView);
- }
+ private LayoutParams createOrReuseLayoutParams(View v) {
+ final LayoutParams currentLp = (LayoutParams) v.getLayoutParams();
+ if (currentLp instanceof LayoutParams) {
+ return currentLp;
+ }
+ return new LayoutParams(v);
+ }
- // If the current view is still being animated, we should
- // force the animation to end
- if (mCurrentView != null) {
- mCurrentView.clearAnimation();
- }
+ void showOnly(int childIndex, boolean animate, boolean onLayout) {
+ if (mAdapter == null) return;
- // load the new mCurrentView from our adapter
- mPreviousView = mCurrentView;
- mCurrentView = mAdapter.getView(childIndex, null, this);
- if (mPreviousView != mCurrentView) {
- addViewInLayout(mCurrentView, 0, makeLayoutParams(), true);
- mCurrentView.bringToFront();
- }
+ for (int i = 0; i < mPreviousViews.size(); i++) {
+ View viewToRemove = mPreviousViews.get(i);
+ viewToRemove.clearAnimation();
+ // applyTransformForChildAtIndex here just allows for any cleanup
+ // associated with this view that may need to be done by a subclass
+ applyTransformForChildAtIndex(viewToRemove, -1);
+ removeViewInLayout(viewToRemove);
+ }
+ mPreviousViews.clear();
+ int newWindowStartUnbounded = childIndex - mActiveOffset;
+ int newWindowEndUnbounded = newWindowStartUnbounded + mNumActiveViews - 1;
+ int newWindowStart = Math.max(0, newWindowStartUnbounded);
+ int newWindowEnd = Math.min(mAdapter.getCount(), newWindowEndUnbounded);
-
-
- // Animate as necessary
- if (mPreviousView != null && mPreviousView != mCurrentView) {
- if (animate && mOutAnimation != null) {
- mPreviousView.startAnimation(mOutAnimation);
+ // This section clears out any items that are in our mActiveViews list
+ // but are outside the effective bounds of our window (this is becomes an issue
+ // at the extremities of the list, eg. where newWindowStartUnbounded < 0 or
+ // newWindowEndUnbounded > mAdapter.getCount() - 1
+ for (int i = newWindowStartUnbounded; i < newWindowEndUnbounded; i++) {
+ if (i < newWindowStart || i > newWindowEnd) {
+ int index = modulo(i, mNumActiveViews);
+ if (mActiveViews[index] != null) {
+ View previousView = mActiveViews[index];
+ mPreviousViews.add(previousView);
+ int previousViewRelativeIndex = modulo(index - mCurrentWindowStart,
+ mNumActiveViews);
+ animateViewForTransition(previousViewRelativeIndex, -1, previousView);
}
- // This line results in the view becoming invisible *after*
- // the above animation is complete, or, if there is no animation
- // then it becomes invisble immediately
- mPreviousView.setVisibility(View.GONE);
}
+ }
- if (mCurrentView != null && animate && mInAnimation != null) {
- mCurrentView.startAnimation(mInAnimation);
- }
+ // If the window has changed
+ if (! (newWindowStart == mCurrentWindowStart && newWindowEnd == mCurrentWindowEnd)) {
+ // Run through the indices in the new range
+ for (int i = newWindowStart; i <= newWindowEnd; i++) {
- mFirstTime = false;
- if (!onLayout) {
- requestLayout();
- invalidate();
- } else {
- // If the Adapter tries to layout the current view when we get it using getView above
- // the layout will end up being ignored since we are currently laying out, so
- // we post a delayed requestLayout and invalidate
- mMainQueue.post(new Runnable() {
- @Override
- public void run() {
- mCurrentView.requestLayout();
- mCurrentView.invalidate();
+ int oldRelativeIndex = i - mCurrentWindowStartUnbounded;
+ int newRelativeIndex = i - newWindowStartUnbounded;
+ int index = modulo(i, mNumActiveViews);
+
+ // If this item is in the current window, great, we just need to apply
+ // the transform for it's new relative position in the window, and animate
+ // between it's current and new relative positions
+ if (i >= mCurrentWindowStart && i <= mCurrentWindowEnd) {
+ View view = mActiveViews[index];
+ applyTransformForChildAtIndex(view, newRelativeIndex);
+ animateViewForTransition(oldRelativeIndex, newRelativeIndex, view);
+
+ // Otherwise this view is new, so first we have to displace the view that's
+ // taking the new view's place within our cache (a circular array)
+ } else {
+ if (mActiveViews[index] != null) {
+ View previousView = mActiveViews[index];
+ mPreviousViews.add(previousView);
+ int previousViewRelativeIndex = modulo(index - mCurrentWindowStart,
+ mNumActiveViews);
+ animateViewForTransition(previousViewRelativeIndex, -1, previousView);
+
+ if (mCurrentWindowStart > newWindowStart) {
+ mViewsToBringToFront.add(previousView);
+ }
}
- });
+
+ // We've cleared a spot for the new view. Get it from the adapter, add it
+ // and apply any transform / animation
+ View newView = mAdapter.getView(i, null, this);
+ if (newView != null) {
+ mActiveViews[index] = newView;
+ addViewInLayout(newView, -1, createOrReuseLayoutParams(newView));
+ applyTransformForChildAtIndex(newView, newRelativeIndex);
+ animateViewForTransition(-1, newRelativeIndex, newView);
+ }
+ }
+ mActiveViews[index].bringToFront();
}
+
+ for (int i = 0; i < mViewsToBringToFront.size(); i++) {
+ View v = mViewsToBringToFront.get(i);
+ v.bringToFront();
+ }
+ mViewsToBringToFront.clear();
+
+ mCurrentWindowStart = newWindowStart;
+ mCurrentWindowEnd = newWindowEnd;
+ mCurrentWindowStartUnbounded = newWindowStartUnbounded;
+ }
+
+ mFirstTime = false;
+ if (!onLayout) {
+ requestLayout();
+ invalidate();
+ } else {
+ // If the Adapter tries to layout the current view when we get it using getView
+ // above the layout will end up being ignored since we are currently laying out, so
+ // we post a delayed requestLayout and invalidate
+ mMainQueue.post(new Runnable() {
+ @Override
+ public void run() {
+ requestLayout();
+ invalidate();
+ }
+ });
}
}
@@ -247,10 +460,11 @@
int childRight = mPaddingLeft + child.getMeasuredWidth();
int childBottom = mPaddingTop + child.getMeasuredHeight();
+ LayoutParams lp = (LayoutParams) child.getLayoutParams();
- child.layout(mPaddingLeft, mPaddingTop, childRight, childBottom);
+ child.layout(mPaddingLeft + lp.horizontalOffset, mPaddingTop + lp.verticalOffset,
+ childRight + lp.horizontalOffset, childBottom + lp.verticalOffset);
}
-
mDataChanged = false;
}
@@ -261,8 +475,6 @@
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
- Log.v(TAG, "onMeasure");
-
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
@@ -278,7 +490,6 @@
child.measure(childWidthMeasureSpec, childheightMeasureSpec);
}
-
setMeasuredDimension(widthSpecSize, heightSpecSize);
}
@@ -302,7 +513,7 @@
* @see #getDisplayedChild()
*/
public View getCurrentView() {
- return mCurrentView;
+ return getViewAtRelativeIndex(mActiveOffset);
}
/**
@@ -402,22 +613,25 @@
@Override
public void setAdapter(Adapter adapter) {
+ if (mAdapter != null && mDataSetObserver != null) {
+ mAdapter.unregisterDataSetObserver(mDataSetObserver);
+ }
+
mAdapter = adapter;
if (mAdapter != null) {
- if (mDataSetObserver != null) {
- mAdapter.unregisterDataSetObserver(mDataSetObserver);
- }
-
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
}
+ setFocusable(true);
}
/**
- * Sets up this AdapterViewAnimator to use a remote views adapter which connects to a RemoteViewsService
- * through the specified intent.
- * @param intent the intent used to identify the RemoteViewsService for the adapter to connect to.
+ * Sets up this AdapterViewAnimator to use a remote views adapter which connects to a
+ * RemoteViewsService through the specified intent.
+ *
+ * @param intent the intent used to identify the RemoteViewsService for the adapter to
+ * connect to.
*/
@android.view.RemotableViewMethod
public void setRemoteViewsAdapter(Intent intent) {
@@ -431,7 +645,7 @@
@Override
public View getSelectedView() {
- return mCurrentView;
+ return getViewAtRelativeIndex(mActiveOffset);
}
/**
@@ -452,4 +666,61 @@
setAdapter(mRemoteViewsAdapter);
}
}
+
+ static class LayoutParams extends ViewGroup.LayoutParams {
+ int horizontalOffset;
+ int verticalOffset;
+ View mView;
+
+ LayoutParams(View view) {
+ super(0, 0);
+ horizontalOffset = 0;
+ verticalOffset = 0;
+ mView = view;
+ }
+
+ LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+ horizontalOffset = 0;
+ verticalOffset = 0;
+ }
+
+ void setHorizontalOffset(int newHorizontalOffset) {
+ horizontalOffset = newHorizontalOffset;
+ if (mView != null) {
+ mView.requestLayout();
+ mView.invalidate();
+ }
+ }
+
+ private Rect parentRect = new Rect();
+ void invalidateGlobalRegion(View v, Rect r) {
+ View p = v;
+ boolean firstPass = true;
+ parentRect.set(0, 0, 0, 0);
+ while (p.getParent() != null && p.getParent() instanceof View
+ && !parentRect.contains(r)) {
+ if (!firstPass) r.offset(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY());
+ firstPass = false;
+ p = (View) p.getParent();
+ parentRect.set(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY(),
+ p.getRight() - p.getScrollX(), p.getBottom() - p.getScrollY());
+ }
+ p.invalidate(r.left, r.top, r.right, r.bottom);
+ }
+
+ private Rect invalidateRect = new Rect();
+ // This is public so that PropertyAnimator can access it
+ public void setVerticalOffset(int newVerticalOffset) {
+ int offsetDelta = newVerticalOffset - verticalOffset;
+ verticalOffset = newVerticalOffset;
+ if (mView != null) {
+ mView.requestLayout();
+ int top = Math.min(mView.getTop() + offsetDelta, mView.getTop());
+ int bottom = Math.max(mView.getBottom() + offsetDelta, mView.getBottom());
+ invalidateRect.set(mView.getLeft(), top, mView.getRight(), bottom);
+ invalidateGlobalRegion(mView, invalidateRect);
+ }
+ }
+ }
}
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index e27bb4fe..e445180 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -46,27 +46,32 @@
*/
@RemoteView
public class FrameLayout extends ViewGroup {
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "measurement")
boolean mMeasureAllChildren = false;
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "drawing")
private Drawable mForeground;
- @ViewDebug.ExportedProperty
+
+ @ViewDebug.ExportedProperty(category = "padding")
private int mForegroundPaddingLeft = 0;
- @ViewDebug.ExportedProperty
+
+ @ViewDebug.ExportedProperty(category = "padding")
private int mForegroundPaddingTop = 0;
- @ViewDebug.ExportedProperty
+
+ @ViewDebug.ExportedProperty(category = "padding")
private int mForegroundPaddingRight = 0;
- @ViewDebug.ExportedProperty
+
+ @ViewDebug.ExportedProperty(category = "padding")
private int mForegroundPaddingBottom = 0;
private final Rect mSelfBounds = new Rect();
private final Rect mOverlayBounds = new Rect();
- @ViewDebug.ExportedProperty
+
+ @ViewDebug.ExportedProperty(category = "drawing")
private int mForegroundGravity = Gravity.FILL;
/** {@hide} */
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "drawing")
protected boolean mForegroundInPadding = true;
boolean mForegroundBoundsChanged = false;
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 7254c3c..53187bf 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -57,7 +57,7 @@
* Whether the children of this layout are baseline aligned. Only applicable
* if {@link #mOrientation} is horizontal.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
private boolean mBaselineAligned = true;
/**
@@ -67,7 +67,7 @@
* Note: this is orthogonal to {@link #mBaselineAligned}, which is concerned
* with whether the children of this layout are baseline aligned.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
private int mBaselineAlignedChildIndex = -1;
/**
@@ -75,12 +75,13 @@
* We'll calculate the baseline of this layout as we measure vertically; for
* horizontal linear layouts, the offset of 0 is appropriate.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "measurement")
private int mBaselineChildTop = 0;
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "measurement")
private int mOrientation;
- @ViewDebug.ExportedProperty(mapping = {
+
+ @ViewDebug.ExportedProperty(category = "measurement", mapping = {
@ViewDebug.IntToString(from = -1, to = "NONE"),
@ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"),
@ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"),
@@ -95,13 +96,14 @@
@ViewDebug.IntToString(from = Gravity.FILL, to = "FILL")
})
private int mGravity = Gravity.LEFT | Gravity.TOP;
- @ViewDebug.ExportedProperty
+
+ @ViewDebug.ExportedProperty(category = "measurement")
private int mTotalLength;
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
private float mWeightSum;
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
private boolean mUseLargestChild;
private int[] mMaxAscent;
@@ -1401,7 +1403,7 @@
* 0 if the view should not be stretched. Otherwise the extra pixels
* will be pro-rated among all views whose weight is greater than 0.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public float weight;
/**
@@ -1409,7 +1411,7 @@
*
* @see android.view.Gravity
*/
- @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.ExportedProperty(category = "layout", mapping = {
@ViewDebug.IntToString(from = -1, to = "NONE"),
@ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"),
@ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"),
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 0bb41e5..0d0a1ba 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -1186,7 +1186,7 @@
* UNSPECIFIED/AT_MOST modes, false otherwise.
* @hide
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "list")
protected boolean recycleOnMeasure() {
return true;
}
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index d404ce7..b562942 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -122,7 +122,7 @@
private OnScrollChangedListener mOnScrollChangedListener =
new OnScrollChangedListener() {
public void onScrollChanged() {
- View anchor = mAnchor.get();
+ View anchor = mAnchor != null ? mAnchor.get() : null;
if (anchor != null && mPopupView != null) {
WindowManager.LayoutParams p = (WindowManager.LayoutParams)
mPopupView.getLayoutParams();
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 71f0c2f..0f52fc8 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -336,7 +336,7 @@
*
* @return true if the progress bar is in indeterminate mode
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "progress")
public synchronized boolean isIndeterminate() {
return mIndeterminate;
}
@@ -609,7 +609,7 @@
* @see #setMax(int)
* @see #getMax()
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "progress")
public synchronized int getProgress() {
return mIndeterminate ? 0 : mProgress;
}
@@ -626,7 +626,7 @@
* @see #setMax(int)
* @see #getMax()
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "progress")
public synchronized int getSecondaryProgress() {
return mIndeterminate ? 0 : mSecondaryProgress;
}
@@ -640,7 +640,7 @@
* @see #getProgress()
* @see #getSecondaryProgress()
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "progress")
public synchronized int getMax() {
return mMax;
}
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 1aa1df3..64cda49 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -1011,7 +1011,7 @@
* @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerVertical
*/
public static class LayoutParams extends ViewGroup.MarginLayoutParams {
- @ViewDebug.ExportedProperty(resolveId = true, indexMapping = {
+ @ViewDebug.ExportedProperty(category = "layout", resolveId = true, indexMapping = {
@ViewDebug.IntToString(from = ABOVE, to = "above"),
@ViewDebug.IntToString(from = ALIGN_BASELINE, to = "alignBaseline"),
@ViewDebug.IntToString(from = ALIGN_BOTTOM, to = "alignBottom"),
@@ -1040,7 +1040,7 @@
* When true, uses the parent as the anchor if the anchor doesn't exist or if
* the anchor's visibility is GONE.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public boolean alignWithParent;
public LayoutParams(Context c, AttributeSet attrs) {
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 52635e8..ebf5d6e 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -433,6 +433,7 @@
int cacheIndex = getCacheIndex(position);
FrameLayout flipper = mViewCache[cacheIndex].flipper;
flipper.setVisibility(View.VISIBLE);
+ flipper.setAlpha(1.0f);
if (indexInfo == null) {
// hide the item view and show the loading view
diff --git a/core/java/android/widget/ResourceCursorAdapter.java b/core/java/android/widget/ResourceCursorAdapter.java
index c9c217a..aee411e 100644
--- a/core/java/android/widget/ResourceCursorAdapter.java
+++ b/core/java/android/widget/ResourceCursorAdapter.java
@@ -36,9 +36,8 @@
/**
* Constructor.
- *
- * @param context The context where the ListView associated with this
- * SimpleListItemFactory is running
+ *
+ * @param context The context where the ListView associated with this adapter is running
* @param layout resource identifier of a layout file that defines the views
* for this list item. Unless you override them later, this will
* define both the item views and the drop down views.
@@ -51,9 +50,8 @@
/**
* Constructor.
- *
- * @param context The context where the ListView associated with this
- * SimpleListItemFactory is running
+ *
+ * @param context The context where the ListView associated with this adapter is running
* @param layout resource identifier of a layout file that defines the views
* for this list item. Unless you override them later, this will
* define both the item views and the drop down views.
@@ -69,6 +67,22 @@
}
/**
+ * Constructor.
+ *
+ * @param context The context where the ListView associated with this adapter is running
+ * @param layout resource identifier of a layout file that defines the views
+ * for this list item. Unless you override them later, this will
+ * define both the item views and the drop down views.
+ * @param c The cursor from which to get the data.
+ * @param flags flags used to determine the behavior of the adapter
+ */
+ public ResourceCursorAdapter(Context context, int layout, Cursor c, int flags) {
+ super(context, c, flags);
+ mLayout = mDropDownLayout = layout;
+ mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ /**
* Inflates view(s) from the specified XML file.
*
* @see android.widget.CursorAdapter#newView(android.content.Context,
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
new file mode 100644
index 0000000..4cd44d9
--- /dev/null
+++ b/core/java/android/widget/StackView.java
@@ -0,0 +1,487 @@
+/*
+ * 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.widget;
+
+import java.util.WeakHashMap;
+
+import android.animation.PropertyAnimator;
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.widget.RemoteViews.RemoteView;
+
+@RemoteView
+/**
+ * A view that displays its children in a stack and allows users to discretely swipe
+ * through the children.
+ */
+public class StackView extends AdapterViewAnimator {
+ private final String TAG = "StackView";
+
+ /**
+ * Default animation parameters
+ */
+ private final int DEFAULT_ANIMATION_DURATION = 400;
+ private final int MINIMUM_ANIMATION_DURATION = 50;
+
+ /**
+ * These specify the different gesture states
+ */
+ private final int GESTURE_NONE = 0;
+ private final int GESTURE_SLIDE_UP = 1;
+ private final int GESTURE_SLIDE_DOWN = 2;
+
+ /**
+ * Specifies how far you need to swipe (up or down) before it
+ * will be consider a completed gesture when you lift your finger
+ */
+ private final float SWIPE_THRESHOLD_RATIO = 0.35f;
+ private final float SLIDE_UP_RATIO = 0.7f;
+
+ private final WeakHashMap<View, Float> mRotations = new WeakHashMap<View, Float>();
+ private final WeakHashMap<View, Integer>
+ mChildrenToApplyTransformsTo = new WeakHashMap<View, Integer>();
+
+ /**
+ * Sentinel value for no current active pointer.
+ * Used by {@link #mActivePointerId}.
+ */
+ private static final int INVALID_POINTER = -1;
+
+ /**
+ * These variables are all related to the current state of touch interaction
+ * with the stack
+ */
+ private boolean mGestureComplete = false;
+ private float mInitialY;
+ private float mInitialX;
+ private int mActivePointerId;
+ private int mYOffset = 0;
+ private int mYVelocity = 0;
+ private int mSwipeGestureType = GESTURE_NONE;
+ private int mViewHeight;
+ private int mSwipeThreshold;
+ private int mTouchSlop;
+ private int mMaximumVelocity;
+ private VelocityTracker mVelocityTracker;
+
+ private boolean mFirstLayoutHappened = false;
+
+ // TODO: temp hack to get this thing started
+ int mIndex = 5;
+
+ public StackView(Context context) {
+ super(context);
+ initStackView();
+ }
+
+ public StackView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initStackView();
+ }
+
+ private void initStackView() {
+ configureViewAnimator(4, 2);
+ setStaticTransformationsEnabled(true);
+ final ViewConfiguration configuration = ViewConfiguration.get(getContext());
+ mTouchSlop = configuration.getScaledTouchSlop();// + 5;
+ mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+ mActivePointerId = INVALID_POINTER;
+ }
+
+ /**
+ * Animate the views between different relative indexes within the {@link AdapterViewAnimator}
+ */
+ void animateViewForTransition(int fromIndex, int toIndex, View view) {
+ if (fromIndex == -1 && toIndex == 0) {
+ // Fade item in
+ if (view.getAlpha() == 1) {
+ view.setAlpha(0);
+ }
+ PropertyAnimator fadeIn = new PropertyAnimator(DEFAULT_ANIMATION_DURATION,
+ view, "alpha", view.getAlpha(), 1.0f);
+ fadeIn.start();
+ } else if (fromIndex == mNumActiveViews - 1 && toIndex == mNumActiveViews - 2) {
+ // Slide item in
+ view.setVisibility(VISIBLE);
+ LayoutParams lp = (LayoutParams) view.getLayoutParams();
+
+ int largestDuration = (int) Math.round(
+ (lp.verticalOffset*1.0f/-mViewHeight)*DEFAULT_ANIMATION_DURATION);
+ int duration = largestDuration;
+ if (mYVelocity != 0) {
+ duration = 1000*(0 - lp.verticalOffset)/Math.abs(mYVelocity);
+ }
+
+ duration = Math.min(duration, largestDuration);
+ duration = Math.max(duration, MINIMUM_ANIMATION_DURATION);
+
+ PropertyAnimator slideDown = new PropertyAnimator(duration, lp,
+ "verticalOffset", lp.verticalOffset, 0);
+ slideDown.start();
+
+ PropertyAnimator fadeIn = new PropertyAnimator(duration, view,
+ "alpha", view.getAlpha(), 1.0f);
+ fadeIn.start();
+ } else if (fromIndex == mNumActiveViews - 2 && toIndex == mNumActiveViews - 1) {
+ // Slide item out
+ LayoutParams lp = (LayoutParams) view.getLayoutParams();
+
+ int largestDuration = (int) Math.round(
+ (1 - (lp.verticalOffset*1.0f/-mViewHeight))*DEFAULT_ANIMATION_DURATION);
+ int duration = largestDuration;
+ if (mYVelocity != 0) {
+ duration = 1000*(lp.verticalOffset + mViewHeight)/Math.abs(mYVelocity);
+ }
+
+ duration = Math.min(duration, largestDuration);
+ duration = Math.max(duration, MINIMUM_ANIMATION_DURATION);
+
+ PropertyAnimator slideUp = new PropertyAnimator(duration, lp,
+ "verticalOffset", lp.verticalOffset, -mViewHeight);
+ slideUp.start();
+
+ PropertyAnimator fadeOut = new PropertyAnimator(duration, view,
+ "alpha", view.getAlpha(), 0.0f);
+ fadeOut.start();
+ } else if (fromIndex == -1 && toIndex == mNumActiveViews - 1) {
+ // Make sure this view that is "waiting in the wings" is invisible
+ view.setAlpha(0.0f);
+ } else if (toIndex == -1) {
+ // Fade item out
+ PropertyAnimator fadeOut = new PropertyAnimator(DEFAULT_ANIMATION_DURATION,
+ view, "alpha", view.getAlpha(), 0);
+ fadeOut.start();
+ }
+ }
+
+ /**
+ * Apply any necessary tranforms for the child that is being added.
+ */
+ void applyTransformForChildAtIndex(View child, int relativeIndex) {
+ float rotation;
+
+ if (!mRotations.containsKey(child)) {
+ rotation = (float) (Math.random()*26 - 13);
+ mRotations.put(child, rotation);
+ } else {
+ rotation = mRotations.get(child);
+ }
+
+ // Child has been removed
+ if (relativeIndex == -1) {
+ if (mRotations.containsKey(child)) {
+ mRotations.remove(child);
+ }
+ if (mChildrenToApplyTransformsTo.containsKey(child)) {
+ mChildrenToApplyTransformsTo.remove(child);
+ }
+ }
+
+ // if this view is already in the layout, we need to
+ // wait until layout has finished in order to set the
+ // pivot point of the rotation (requiring getMeasuredWidth/Height())
+ mChildrenToApplyTransformsTo.put(child, relativeIndex);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+
+ if (!mChildrenToApplyTransformsTo.isEmpty()) {
+ for (View child: mChildrenToApplyTransformsTo.keySet()) {
+ if (mRotations.containsKey(child)) {
+ child.setPivotX(child.getMeasuredWidth()/2);
+ child.setPivotY(child.getMeasuredHeight()/2);
+ child.setRotation(mRotations.get(child));
+ }
+ }
+ mChildrenToApplyTransformsTo.clear();
+ }
+
+ if (!mFirstLayoutHappened) {
+ mViewHeight = (int) Math.round(SLIDE_UP_RATIO*getMeasuredHeight());
+ mSwipeThreshold = (int) Math.round(SWIPE_THRESHOLD_RATIO*mViewHeight);
+
+ // TODO: Right now this walks all the way up the view hierarchy and disables
+ // ClipChildren and ClipToPadding. We're probably going to want to reset
+ // these flags as well.
+ setClipChildren(false);
+ ViewGroup view = this;
+ while (view.getParent() != null && view.getParent() instanceof ViewGroup) {
+ view = (ViewGroup) view.getParent();
+ view.setClipChildren(false);
+ view.setClipToPadding(false);
+ }
+
+ mFirstLayoutHappened = true;
+ }
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ int action = ev.getAction();
+ switch(action & MotionEvent.ACTION_MASK) {
+
+ case MotionEvent.ACTION_DOWN: {
+ if (mActivePointerId == INVALID_POINTER) {
+ mInitialX = ev.getX();
+ mInitialY = ev.getY();
+ mActivePointerId = ev.getPointerId(0);
+ }
+ break;
+ }
+ case MotionEvent.ACTION_MOVE: {
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (pointerIndex == INVALID_POINTER) {
+ // no data for our primary pointer, this shouldn't happen, log it
+ Log.d(TAG, "Error: No data for our primary pointer.");
+ return false;
+ }
+
+ float newY = ev.getY(pointerIndex);
+ float deltaY = newY - mInitialY;
+
+ if ((int) Math.abs(deltaY) > mTouchSlop && mSwipeGestureType == GESTURE_NONE) {
+ mSwipeGestureType = deltaY < 0 ? GESTURE_SLIDE_UP : GESTURE_SLIDE_DOWN;
+ mGestureComplete = false;
+ cancelLongPress();
+ requestDisallowInterceptTouchEvent(true);
+ }
+ break;
+ }
+ case MotionEvent.ACTION_POINTER_UP: {
+ onSecondaryPointerUp(ev);
+ break;
+ }
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL: {
+ mActivePointerId = INVALID_POINTER;
+ mSwipeGestureType = GESTURE_NONE;
+ mGestureComplete = true;
+ }
+ }
+
+ return mSwipeGestureType != GESTURE_NONE;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ int action = ev.getAction();
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (pointerIndex == INVALID_POINTER) {
+ // no data for our primary pointer, this shouldn't happen, log it
+ Log.d(TAG, "Error: No data for our primary pointer.");
+ return false;
+ }
+
+ float newY = ev.getY(pointerIndex);
+ float deltaY = newY - mInitialY;
+
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ mVelocityTracker.addMovement(ev);
+
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_MOVE: {
+ if ((int) Math.abs(deltaY) > mTouchSlop && mSwipeGestureType == GESTURE_NONE) {
+ mSwipeGestureType = deltaY < 0 ? GESTURE_SLIDE_UP : GESTURE_SLIDE_DOWN;
+ mGestureComplete = false;
+ cancelLongPress();
+ requestDisallowInterceptTouchEvent(true);
+ }
+
+ if (!mGestureComplete) {
+ if (mSwipeGestureType == GESTURE_SLIDE_DOWN) {
+ View v = getViewAtRelativeIndex(mNumActiveViews - 1);
+ if (v != null) {
+ // This view is present but hidden, make sure it's visible
+ // if they pull down
+ v.setVisibility(VISIBLE);
+
+ float r = (deltaY-mTouchSlop)*1.0f / (mSwipeThreshold);
+ mYOffset = Math.min(-mViewHeight + (int) Math.round(
+ r*mSwipeThreshold) - mTouchSlop, 0);
+ LayoutParams lp = (LayoutParams) v.getLayoutParams();
+ lp.setVerticalOffset(mYOffset);
+
+ float alpha = Math.max(0.0f, 1.0f - (1.0f*mYOffset/-mViewHeight));
+ alpha = Math.min(1.0f, alpha);
+ v.setAlpha(alpha);
+ }
+ return true;
+ } else if (mSwipeGestureType == GESTURE_SLIDE_UP) {
+ View v = getViewAtRelativeIndex(mNumActiveViews - 2);
+
+ if (v != null) {
+ float r = -(deltaY*1.0f + mTouchSlop) / (mSwipeThreshold);
+ mYOffset = Math.min((int) Math.round(r*-mSwipeThreshold), 0);
+ LayoutParams lp = (LayoutParams) v.getLayoutParams();
+ lp.setVerticalOffset(mYOffset);
+
+ float alpha = Math.max(0.0f, 1.0f - (1.0f*mYOffset/-mViewHeight));
+ alpha = Math.min(1.0f, alpha);
+ v.setAlpha(alpha);
+ }
+ return true;
+ }
+ }
+ break;
+ }
+ case MotionEvent.ACTION_UP: {
+ handlePointerUp(ev);
+ break;
+ }
+ case MotionEvent.ACTION_POINTER_UP: {
+ onSecondaryPointerUp(ev);
+ break;
+ }
+ case MotionEvent.ACTION_CANCEL: {
+ mActivePointerId = INVALID_POINTER;
+ mGestureComplete = true;
+ mSwipeGestureType = GESTURE_NONE;
+ mYOffset = 0;
+ break;
+ }
+ }
+ return true;
+ }
+
+ private final Rect touchRect = new Rect();
+ private void onSecondaryPointerUp(MotionEvent ev) {
+ final int activePointerIndex = ev.getActionIndex();
+ final int pointerId = ev.getPointerId(activePointerIndex);
+ if (pointerId == mActivePointerId) {
+
+ int activeViewIndex = (mSwipeGestureType == GESTURE_SLIDE_DOWN) ? mNumActiveViews - 1
+ : mNumActiveViews - 2;
+
+ View v = getViewAtRelativeIndex(activeViewIndex);
+ if (v == null) return;
+
+ // Our primary pointer has gone up -- let's see if we can find
+ // another pointer on the view. If so, then we should replace
+ // our primary pointer with this new pointer and adjust things
+ // so that the view doesn't jump
+ for (int index = 0; index < ev.getPointerCount(); index++) {
+ if (index != activePointerIndex) {
+
+ float x = ev.getX(index);
+ float y = ev.getY(index);
+
+ touchRect.set(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
+ if (touchRect.contains((int) Math.round(x), (int) Math.round(y))) {
+ float oldX = ev.getX(activePointerIndex);
+ float oldY = ev.getY(activePointerIndex);
+
+ // adjust our frame of reference to avoid a jump
+ mInitialY += (y - oldY);
+ mInitialX += (x - oldX);
+
+ mActivePointerId = ev.getPointerId(index);
+ if (mVelocityTracker != null) {
+ mVelocityTracker.clear();
+ }
+ // ok, we're good, we found a new pointer which is touching the active view
+ return;
+ }
+ }
+ }
+ // if we made it this far, it means we didn't find a satisfactory new pointer :(,
+ // so end the
+ handlePointerUp(ev);
+ }
+ }
+
+ private void handlePointerUp(MotionEvent ev) {
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ float newY = ev.getY(pointerIndex);
+ int deltaY = (int) (newY - mInitialY);
+
+ mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+ mYVelocity = (int) mVelocityTracker.getYVelocity(mActivePointerId);
+
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+
+ if (deltaY > mSwipeThreshold && mSwipeGestureType == GESTURE_SLIDE_DOWN &&
+ !mGestureComplete) {
+ // Swipe threshold exceeded, swipe down
+ showNext();
+ } else if (deltaY < -mSwipeThreshold && mSwipeGestureType == GESTURE_SLIDE_UP &&
+ !mGestureComplete) {
+ // Swipe threshold exceeded, swipe up
+ showPrevious();
+ } else if (mSwipeGestureType == GESTURE_SLIDE_UP && !mGestureComplete) {
+ // Didn't swipe up far enough, snap back down
+ View v = getViewAtRelativeIndex(mNumActiveViews - 2);
+ if (v != null) {
+ // Compute the animation duration based on how far they pulled it up
+ LayoutParams lp = (LayoutParams) v.getLayoutParams();
+ int duration = (int) Math.round(
+ lp.verticalOffset*1.0f/-mViewHeight*DEFAULT_ANIMATION_DURATION);
+ duration = Math.max(MINIMUM_ANIMATION_DURATION, duration);
+
+ // Animate back down
+ PropertyAnimator slideDown = new PropertyAnimator(duration, lp,
+ "verticalOffset", lp.verticalOffset, 0);
+ slideDown.start();
+ PropertyAnimator fadeIn = new PropertyAnimator(duration, v,
+ "alpha",v.getAlpha(), 1.0f);
+ fadeIn.start();
+ }
+ } else if (mSwipeGestureType == GESTURE_SLIDE_DOWN && !mGestureComplete) {
+ // Didn't swipe down far enough, snap back up
+ View v = getViewAtRelativeIndex(mNumActiveViews - 1);
+ if (v != null) {
+ // Compute the animation duration based on how far they pulled it down
+ LayoutParams lp = (LayoutParams) v.getLayoutParams();
+ int duration = (int) Math.round(
+ (1 - lp.verticalOffset*1.0f/-mViewHeight)*DEFAULT_ANIMATION_DURATION);
+ duration = Math.max(MINIMUM_ANIMATION_DURATION, duration);
+
+ // Animate back up
+ PropertyAnimator slideUp = new PropertyAnimator(duration, lp,
+ "verticalOffset", lp.verticalOffset, -mViewHeight);
+ slideUp.start();
+ PropertyAnimator fadeOut = new PropertyAnimator(duration, v,
+ "alpha",v.getAlpha(), 0.0f);
+ fadeOut.start();
+ }
+ }
+
+ mActivePointerId = INVALID_POINTER;
+ mGestureComplete = true;
+ mSwipeGestureType = GESTURE_NONE;
+ mYOffset = 0;
+ }
+
+ @Override
+ public void onRemoteAdapterConnected() {
+ super.onRemoteAdapterConnected();
+ setDisplayedChild(mIndex);
+ }
+}
diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java
index 48d12df..b612004 100644
--- a/core/java/android/widget/TableRow.java
+++ b/core/java/android/widget/TableRow.java
@@ -387,13 +387,13 @@
/**
* <p>The column index of the cell represented by the widget.</p>
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public int column;
/**
* <p>The number of columns the widgets spans over.</p>
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public int span;
private static final int LOCATION = 0;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f3f68f9..b413aee 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -5818,7 +5818,7 @@
/**
* Convenience for {@link Selection#getSelectionStart}.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "text")
public int getSelectionStart() {
return Selection.getSelectionStart(getText());
}
@@ -5826,7 +5826,7 @@
/**
* Convenience for {@link Selection#getSelectionEnd}.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "text")
public int getSelectionEnd() {
return Selection.getSelectionEnd(getText());
}
@@ -8062,7 +8062,7 @@
}
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "text")
private CharSequence mText;
private CharSequence mTransformed;
private BufferType mBufferType = BufferType.NORMAL;
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index 281f32c..6b52409 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -24,8 +24,10 @@
import android.app.ActionBar;
import android.app.Activity;
+import android.app.Dialog;
import android.app.Fragment;
import android.app.FragmentTransaction;
+import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.view.ActionMode;
@@ -54,7 +56,9 @@
private static final int TAB_SWITCH_SHOW_HIDE = 0;
private static final int TAB_SWITCH_ADD_REMOVE = 1;
+ private Context mContext;
private Activity mActivity;
+ private Dialog mDialog;
private ViewAnimator mAnimatorView;
private ActionBarView mActionView;
@@ -88,8 +92,17 @@
};
public ActionBarImpl(Activity activity) {
- final View decor = activity.getWindow().getDecorView();
mActivity = activity;
+ init(activity.getWindow().getDecorView());
+ }
+
+ public ActionBarImpl(Dialog dialog) {
+ mDialog = dialog;
+ init(dialog.getWindow().getDecorView());
+ }
+
+ private void init(View decor) {
+ mContext = decor.getContext();
mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar);
mUpperContextView = (ActionBarContextView) decor.findViewById(
com.android.internal.R.id.action_context_bar);
@@ -109,23 +122,23 @@
@Override
public void setStandardNavigationMode(int titleResId, int subtitleResId) {
- setStandardNavigationMode(mActivity.getString(titleResId),
- mActivity.getString(subtitleResId));
+ setStandardNavigationMode(mContext.getString(titleResId),
+ mContext.getString(subtitleResId));
}
@Override
public void setStandardNavigationMode(int titleResId) {
- setStandardNavigationMode(mActivity.getString(titleResId));
+ setStandardNavigationMode(mContext.getString(titleResId));
}
@Override
public void setTitle(int resId) {
- setTitle(mActivity.getString(resId));
+ setTitle(mContext.getString(resId));
}
@Override
public void setSubtitle(int resId) {
- setSubtitle(mActivity.getString(resId));
+ setSubtitle(mContext.getString(resId));
}
public void setCustomNavigationMode(View view) {
@@ -345,6 +358,10 @@
@Override
public void setTabNavigationMode() {
+ if (mActivity == null) {
+ throw new IllegalStateException(
+ "Tab navigation mode cannot be used outside of an Activity");
+ }
mActionView.setNavigationMode(NAVIGATION_MODE_TABS);
}
@@ -396,7 +413,7 @@
@Override
public MenuInflater getMenuInflater() {
- return new MenuInflater(mActivity);
+ return new MenuInflater(mContext);
}
@Override
@@ -485,7 +502,7 @@
return true;
}
- new MenuPopupHelper(mActivity, subMenu).show();
+ new MenuPopupHelper(mContext, subMenu).show();
return true;
}
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
new file mode 100644
index 0000000..ce5959d
--- /dev/null
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.ImageView;
+
+public class PlatLogoActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ ImageView content = new ImageView(this);
+ content.setImageResource(com.android.internal.R.drawable.platlogo);
+ content.setScaleType(ImageView.ScaleType.FIT_CENTER);
+
+ setContentView(content);
+ }
+}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java
index e281536..1357251 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuView.java
@@ -24,6 +24,7 @@
import android.widget.ImageButton;
import android.widget.LinearLayout;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
/**
@@ -39,6 +40,7 @@
private int mMaxItems;
private boolean mReserveOverflow;
private OverflowMenuButton mOverflowButton;
+ private WeakReference<MenuPopupHelper> mOverflowPopup;
public ActionMenuView(Context context) {
this(context, null);
@@ -145,6 +147,16 @@
if (mOverflowButton != null) {
MenuPopupHelper popup = new MenuPopupHelper(getContext(), mMenu, mOverflowButton, true);
popup.show();
+ mOverflowPopup = new WeakReference<MenuPopupHelper>(popup);
+ return true;
+ }
+ return false;
+ }
+
+ public boolean hideOverflowMenu() {
+ MenuPopupHelper popup = mOverflowPopup != null ? mOverflowPopup.get() : null;
+ if (popup != null) {
+ popup.dismiss();
return true;
}
return false;
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index d703a2f..c641441 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -209,6 +209,13 @@
return false;
}
+ public boolean hideOverflowMenu() {
+ if (mMenuView != null) {
+ return mMenuView.hideOverflowMenu();
+ }
+ return false;
+ }
+
public boolean isOverflowReserved() {
return mMenuView != null && mMenuView.isOverflowReserved();
}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index d1a5ae14..860b5b7 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -139,7 +139,8 @@
android_backup_BackupDataOutput.cpp \
android_backup_FileBackupHelperBase.cpp \
android_backup_BackupHelperDispatcher.cpp \
- android_content_res_ObbScanner.cpp
+ android_content_res_ObbScanner.cpp \
+ android_content_res_Configuration.cpp
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 8b09e5f..2dd17bb 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -167,6 +167,7 @@
extern int register_android_view_KeyEvent(JNIEnv* env);
extern int register_android_view_MotionEvent(JNIEnv* env);
extern int register_android_content_res_ObbScanner(JNIEnv* env);
+extern int register_android_content_res_Configuration(JNIEnv* env);
static AndroidRuntime* gCurRuntime = NULL;
@@ -1331,6 +1332,7 @@
REG_JNI(register_android_view_MotionEvent),
REG_JNI(register_android_content_res_ObbScanner),
+ REG_JNI(register_android_content_res_Configuration),
};
/*
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index 2b98e89..cb1c333 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -149,7 +149,9 @@
storedBounds[2] = x1; storedBounds[3] = y1;
jfloat* storedPositions = new jfloat[count];
uint32_t* storedColors = new uint32_t[count];
- memcpy(storedColors, colorValues, count);
+ for (size_t i = 0; i < count; i++) {
+ storedColors[i] = static_cast<uint32_t>(colorValues[i]);
+ }
if (posArray) {
AutoJavaFloatArray autoPos(env, posArray, count);
@@ -185,8 +187,8 @@
storedPositions[1] = 1.0f;
uint32_t* storedColors = new uint32_t[2];
- storedColors[0] = color0;
- storedColors[1] = color1;
+ storedColors[0] = static_cast<uint32_t>(color0);
+ storedColors[1] = static_cast<uint32_t>(color1);
SkiaShader* skiaShader = new SkiaLinearGradientShader(storedBounds, storedColors,
storedPositions, 2, shader, static_cast<SkShader::TileMode>(tileMode), NULL,
@@ -329,13 +331,8 @@
static SkiaShader* ComposeShader_postCreate2(JNIEnv* env, jobject o, SkShader* shader,
SkiaShader* shaderA, SkiaShader* shaderB, SkPorterDuff::Mode porterDuffMode) {
#ifdef USE_OPENGL_RENDERER
- SkAutoUnref au(SkPorterDuff::CreateXfermode(porterDuffMode));
- SkXfermode* mode = (SkXfermode*) au.get();
- SkXfermode::Mode skiaMode;
- if (!SkXfermode::IsMode(mode, &skiaMode)) {
- skiaMode = SkXfermode::kSrcOver_Mode;
- }
- return new SkiaComposeShader(shaderA, shaderB, skiaMode, shader);
+ SkXfermode::Mode mode = SkPorterDuff::ToXfermodeMode(porterDuffMode);
+ return new SkiaComposeShader(shaderA, shaderB, mode, shader);
#else
return NULL;
#endif
@@ -346,6 +343,7 @@
#ifdef USE_OPENGL_RENDERER
SkXfermode::Mode skiaMode;
if (!SkXfermode::IsMode(mode, &skiaMode)) {
+ // TODO: Support other modes
skiaMode = SkXfermode::kSrcOver_Mode;
}
return new SkiaComposeShader(shaderA, shaderB, skiaMode, shader);
diff --git a/core/jni/android/graphics/TextLayout.cpp b/core/jni/android/graphics/TextLayout.cpp
index 716d960..147e1fa 100644
--- a/core/jni/android/graphics/TextLayout.cpp
+++ b/core/jni/android/graphics/TextLayout.cpp
@@ -221,6 +221,18 @@
}
}
+bool TextLayout::prepareRtlTextRun(const jchar* context, jsize start, jsize& count,
+ jsize contextCount, jchar* shaped) {
+ UErrorCode status = U_ZERO_ERROR;
+ count = shapeRtlText(context, start, count, contextCount, shaped, status);
+ if (U_SUCCESS(status)) {
+ return true;
+ } else {
+ LOG(LOG_WARN, "LAYOUT", "drawTextRun error %d\n", status);
+ }
+ return false;
+}
+
void TextLayout::drawTextRun(SkPaint* paint, const jchar* chars,
jint start, jint count, jint contextCount,
int dirFlags, jfloat x, jfloat y, SkCanvas* canvas) {
@@ -231,12 +243,8 @@
uint8_t rtl = dirFlags & 0x1;
if (rtl) {
SkAutoSTMalloc<80, jchar> buffer(contextCount);
- UErrorCode status = U_ZERO_ERROR;
- count = shapeRtlText(chars, start, count, contextCount, buffer.get(), status);
- if (U_SUCCESS(status)) {
+ if (prepareRtlTextRun(chars, start, count, contextCount, buffer.get())) {
canvas->drawText(buffer.get(), count << 1, x_, y_, *paint);
- } else {
- LOG(LOG_WARN, "LAYOUT", "drawTextRun error %d\n", status);
}
} else {
canvas->drawText(chars + start, count << 1, x_, y_, *paint);
diff --git a/core/jni/android/graphics/TextLayout.h b/core/jni/android/graphics/TextLayout.h
index 3d05e18..8f666c0 100644
--- a/core/jni/android/graphics/TextLayout.h
+++ b/core/jni/android/graphics/TextLayout.h
@@ -66,6 +66,9 @@
static bool prepareText(SkPaint *paint, const jchar* text, jsize len, jint bidiFlags,
const jchar** outText, int32_t* outBytes, jchar** outBuffer);
+ static bool prepareRtlTextRun(const jchar* context, jsize start, jsize& count,
+ jsize contextCount, jchar* shaped);
+
private:
static bool needsLayout(const jchar* text, jint len, jint bidiFlags);
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 1feb3b3..0932473a 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -600,7 +600,7 @@
static jint
loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQueue,
jstring internalDataDir, jstring externalDataDir, int sdkVersion,
- jobject jAssetMgr)
+ jobject jAssetMgr, jbyteArray savedState)
{
LOG_TRACE("loadNativeCode_native");
@@ -666,7 +666,18 @@
code->assetManager = assetManagerForJavaObject(env, jAssetMgr);
- code->createActivityFunc(code, NULL, 0);
+ jbyte* rawSavedState = NULL;
+ jsize rawSavedSize = 0;
+ if (savedState != NULL) {
+ rawSavedState = env->GetByteArrayElements(savedState, NULL);
+ rawSavedSize = env->GetArrayLength(savedState);
+ }
+
+ code->createActivityFunc(code, rawSavedState, rawSavedSize);
+
+ if (rawSavedState != NULL) {
+ env->ReleaseByteArrayElements(savedState, rawSavedState, 0);
+ }
}
return (jint)code;
@@ -706,17 +717,31 @@
}
}
-static void
+static jbyteArray
onSaveInstanceState_native(JNIEnv* env, jobject clazz, jint handle)
{
LOG_TRACE("onSaveInstanceState_native");
+
+ jbyteArray array = NULL;
+
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
if (code->callbacks.onSaveInstanceState != NULL) {
size_t len = 0;
- code->callbacks.onSaveInstanceState(code, &len);
+ jbyte* state = (jbyte*)code->callbacks.onSaveInstanceState(code, &len);
+ if (len > 0) {
+ array = env->NewByteArray(len);
+ if (array != NULL) {
+ env->SetByteArrayRegion(array, 0, len, state);
+ }
+ }
+ if (state != NULL) {
+ free(state);
+ }
}
}
+
+ return array;
}
static void
@@ -744,6 +769,18 @@
}
static void
+onConfigurationChanged_native(JNIEnv* env, jobject clazz, jint handle)
+{
+ LOG_TRACE("onConfigurationChanged_native");
+ if (handle != 0) {
+ NativeCode* code = (NativeCode*)handle;
+ if (code->callbacks.onConfigurationChanged != NULL) {
+ code->callbacks.onConfigurationChanged(code);
+ }
+ }
+}
+
+static void
onLowMemory_native(JNIEnv* env, jobject clazz, jint handle)
{
LOG_TRACE("onLowMemory_native");
@@ -934,14 +971,15 @@
}
static const JNINativeMethod g_methods[] = {
- { "loadNativeCode", "(Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;)I",
+ { "loadNativeCode", "(Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;[B)I",
(void*)loadNativeCode_native },
{ "unloadNativeCode", "(I)V", (void*)unloadNativeCode_native },
{ "onStartNative", "(I)V", (void*)onStart_native },
{ "onResumeNative", "(I)V", (void*)onResume_native },
- { "onSaveInstanceStateNative", "(I)V", (void*)onSaveInstanceState_native },
+ { "onSaveInstanceStateNative", "(I)[B", (void*)onSaveInstanceState_native },
{ "onPauseNative", "(I)V", (void*)onPause_native },
{ "onStopNative", "(I)V", (void*)onStop_native },
+ { "onConfigurationChangedNative", "(I)V", (void*)onConfigurationChanged_native },
{ "onLowMemoryNative", "(I)V", (void*)onLowMemory_native },
{ "onWindowFocusChangedNative", "(IZ)V", (void*)onWindowFocusChanged_native },
{ "onSurfaceCreatedNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceCreated_native },
diff --git a/core/jni/android_content_res_Configuration.cpp b/core/jni/android_content_res_Configuration.cpp
new file mode 100644
index 0000000..28a43ab
--- /dev/null
+++ b/core/jni/android_content_res_Configuration.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "Configuration"
+
+#include <utils/Log.h>
+#include "utils/misc.h"
+
+#include "jni.h"
+#include <android_runtime/android_content_res_Configuration.h>
+#include "android_runtime/AndroidRuntime.h"
+
+namespace android {
+
+static struct {
+ jclass clazz;
+
+ jfieldID mcc;
+ jfieldID mnc;
+ jfieldID locale;
+ jfieldID screenLayout;
+ jfieldID touchscreen;
+ jfieldID keyboard;
+ jfieldID keyboardHidden;
+ jfieldID hardKeyboardHidden;
+ jfieldID navigation;
+ jfieldID navigationHidden;
+ jfieldID orientation;
+ jfieldID uiMode;
+} gConfigurationClassInfo;
+
+void android_Configuration_getFromJava(
+ JNIEnv* env, jobject clazz, struct AConfiguration* out) {
+ out->mcc = env->GetIntField(clazz, gConfigurationClassInfo.mcc);
+ out->mnc = env->GetIntField(clazz, gConfigurationClassInfo.mnc);
+ out->screenLayout = env->GetIntField(clazz, gConfigurationClassInfo.screenLayout);
+ out->touchscreen = env->GetIntField(clazz, gConfigurationClassInfo.touchscreen);
+ out->keyboard = env->GetIntField(clazz, gConfigurationClassInfo.keyboard);
+ out->navigation = env->GetIntField(clazz, gConfigurationClassInfo.navigation);
+
+ out->inputFlags = env->GetIntField(clazz, gConfigurationClassInfo.keyboardHidden);
+ int hardKeyboardHidden = env->GetIntField(clazz, gConfigurationClassInfo.hardKeyboardHidden);
+ if (out->inputFlags == ACONFIGURATION_KEYSHIDDEN_NO
+ && hardKeyboardHidden == 2) {
+ out->inputFlags = ACONFIGURATION_KEYSHIDDEN_SOFT;
+ }
+ out->inputFlags |= env->GetIntField(clazz, gConfigurationClassInfo.navigationHidden)
+ << ResTable_config::SHIFT_NAVHIDDEN;
+
+ out->orientation = env->GetIntField(clazz, gConfigurationClassInfo.orientation);
+ out->uiMode = env->GetIntField(clazz, gConfigurationClassInfo.uiMode);
+}
+
+/*
+ * JNI registration.
+ */
+static JNINativeMethod gMethods[] = {
+ /* name, signature, funcPtr */
+ //{ "getObbInfo_native", "(Ljava/lang/String;Landroid/content/res/ObbInfo;)Z",
+ // (void*) android_content_res_ObbScanner_getObbInfo },
+};
+
+#define FIND_CLASS(var, className) \
+ var = env->FindClass(className); \
+ LOG_FATAL_IF(! var, "Unable to find class " className); \
+ var = jclass(env->NewGlobalRef(var));
+
+#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
+ var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find field " fieldName);
+
+int register_android_content_res_Configuration(JNIEnv* env)
+{
+ FIND_CLASS(gConfigurationClassInfo.clazz, "android/content/res/Configuration");
+
+ GET_FIELD_ID(gConfigurationClassInfo.mcc, gConfigurationClassInfo.clazz,
+ "mcc", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.mnc, gConfigurationClassInfo.clazz,
+ "mnc", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.locale, gConfigurationClassInfo.clazz,
+ "locale", "Ljava/util/Locale;");
+ GET_FIELD_ID(gConfigurationClassInfo.screenLayout, gConfigurationClassInfo.clazz,
+ "screenLayout", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.touchscreen, gConfigurationClassInfo.clazz,
+ "touchscreen", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.keyboard, gConfigurationClassInfo.clazz,
+ "keyboard", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.keyboardHidden, gConfigurationClassInfo.clazz,
+ "keyboardHidden", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.hardKeyboardHidden, gConfigurationClassInfo.clazz,
+ "hardKeyboardHidden", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.navigation, gConfigurationClassInfo.clazz,
+ "navigation", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.navigationHidden, gConfigurationClassInfo.clazz,
+ "navigationHidden", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.orientation, gConfigurationClassInfo.clazz,
+ "orientation", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.uiMode, gConfigurationClassInfo.clazz,
+ "uiMode", "I");
+
+ return AndroidRuntime::registerNativeMethods(env, "android/content/res/Configuration", gMethods,
+ NELEM(gMethods));
+}
+
+}; // namespace android
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index bb1a9e3..9f94af9 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -27,6 +27,7 @@
#include <SkPaint.h>
#include <SkRegion.h>
#include <SkScalerContext.h>
+#include <SkTemplates.h>
#include <SkXfermode.h>
#include <OpenGLRenderer.h>
@@ -235,6 +236,16 @@
renderer->drawRect(left, top, right, bottom, paint);
}
+static void android_view_GLES20Canvas_drawRects(JNIEnv* env, jobject canvas,
+ OpenGLRenderer* renderer, SkRegion* region, SkPaint* paint) {
+ SkRegion::Iterator it(*region);
+ while (!it.done()) {
+ const SkIRect& r = it.rect();
+ renderer->drawRect(r.fLeft, r.fTop, r.fRight, r.fBottom, paint);
+ it.next();
+ }
+}
+
static void android_view_GLES20Canvas_drawPath(JNIEnv* env, jobject canvas,
OpenGLRenderer* renderer, SkPath* path, SkPaint* paint) {
renderer->drawPath(path, paint);
@@ -275,6 +286,23 @@
}
}
+static void renderTextRun(OpenGLRenderer* renderer, const jchar* text,
+ jint start, jint count, jint contextCount, jfloat x, jfloat y,
+ int flags, SkPaint* paint) {
+ uint8_t rtl = flags & 0x1;
+ if (rtl) {
+ SkAutoSTMalloc<80, jchar> buffer(contextCount);
+ jchar* shaped = buffer.get();
+ if (TextLayout::prepareRtlTextRun(text, start, count, contextCount, shaped)) {
+ renderer->drawText((const char*) shaped, count << 1, count, x, y, paint);
+ } else {
+ LOGW("drawTextRun error");
+ }
+ } else {
+ renderer->drawText((const char*) (text + start), count << 1, count, x, y, paint);
+ }
+}
+
static void android_view_GLES20Canvas_drawTextArray(JNIEnv* env, jobject canvas,
OpenGLRenderer* renderer, jcharArray text, int index, int count,
jfloat x, jfloat y, int flags, SkPaint* paint) {
@@ -291,6 +319,28 @@
env->ReleaseStringChars(text, textArray);
}
+static void android_view_GLES20Canvas_drawTextRunArray(JNIEnv* env, jobject canvas,
+ OpenGLRenderer* renderer, jcharArray text, int index, int count,
+ int contextIndex, int contextCount, jfloat x, jfloat y, int dirFlags,
+ SkPaint* paint) {
+ jchar* textArray = env->GetCharArrayElements(text, NULL);
+ renderTextRun(renderer, textArray + contextIndex, index - contextIndex,
+ count, contextCount, x, y, dirFlags, paint);
+ env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
+ }
+
+static void android_view_GLES20Canvas_drawTextRun(JNIEnv* env, jobject canvas,
+ OpenGLRenderer* renderer, jstring text, int start, int end,
+ int contextStart, int contextEnd, jfloat x, jfloat y, int dirFlags,
+ SkPaint* paint) {
+ const jchar* textArray = env->GetStringChars(text, NULL);
+ jint count = end - start;
+ jint contextCount = contextEnd - contextStart;
+ renderTextRun(renderer, textArray + contextStart, start - contextStart,
+ count, contextCount, x, y, dirFlags, paint);
+ env->ReleaseStringChars(text, textArray);
+}
+
#endif // USE_OPENGL_RENDERER
// ----------------------------------------------------------------------------
@@ -312,50 +362,55 @@
const char* const kClassPathName = "android/view/GLES20Canvas";
static JNINativeMethod gMethods[] = {
- { "nIsAvailable", "()Z", (void*) android_view_GLES20Canvas_isAvailable },
+ { "nIsAvailable", "()Z", (void*) android_view_GLES20Canvas_isAvailable },
+
#ifdef USE_OPENGL_RENDERER
+ { "nCreateRenderer", "()I", (void*) android_view_GLES20Canvas_createRenderer },
+ { "nDestroyRenderer", "(I)V", (void*) android_view_GLES20Canvas_destroyRenderer },
+ { "nSetViewport", "(III)V", (void*) android_view_GLES20Canvas_setViewport },
+ { "nPrepare", "(I)V", (void*) android_view_GLES20Canvas_prepare },
- { "nCreateRenderer", "()I", (void*) android_view_GLES20Canvas_createRenderer },
- { "nDestroyRenderer", "(I)V", (void*) android_view_GLES20Canvas_destroyRenderer },
- { "nSetViewport", "(III)V", (void*) android_view_GLES20Canvas_setViewport },
- { "nPrepare", "(I)V", (void*) android_view_GLES20Canvas_prepare },
+ { "nSave", "(II)I", (void*) android_view_GLES20Canvas_save },
+ { "nRestore", "(I)V", (void*) android_view_GLES20Canvas_restore },
+ { "nRestoreToCount", "(II)V", (void*) android_view_GLES20Canvas_restoreToCount },
+ { "nGetSaveCount", "(I)I", (void*) android_view_GLES20Canvas_getSaveCount },
- { "nSave", "(II)I", (void*) android_view_GLES20Canvas_save },
- { "nRestore", "(I)V", (void*) android_view_GLES20Canvas_restore },
- { "nRestoreToCount", "(II)V", (void*) android_view_GLES20Canvas_restoreToCount },
- { "nGetSaveCount", "(I)I", (void*) android_view_GLES20Canvas_getSaveCount },
+ { "nSaveLayer", "(IFFFFII)I", (void*) android_view_GLES20Canvas_saveLayer },
+ { "nSaveLayerAlpha", "(IFFFFII)I", (void*) android_view_GLES20Canvas_saveLayerAlpha },
- { "nSaveLayer", "(IFFFFII)I", (void*) android_view_GLES20Canvas_saveLayer },
- { "nSaveLayerAlpha", "(IFFFFII)I", (void*) android_view_GLES20Canvas_saveLayerAlpha },
+ { "nQuickReject", "(IFFFFI)Z", (void*) android_view_GLES20Canvas_quickReject },
+ { "nClipRect", "(IFFFFI)Z", (void*) android_view_GLES20Canvas_clipRectF },
+ { "nClipRect", "(IIIIII)Z", (void*) android_view_GLES20Canvas_clipRect },
- { "nQuickReject", "(IFFFFI)Z", (void*) android_view_GLES20Canvas_quickReject },
- { "nClipRect", "(IFFFFI)Z", (void*) android_view_GLES20Canvas_clipRectF },
- { "nClipRect", "(IIIIII)Z", (void*) android_view_GLES20Canvas_clipRect },
+ { "nTranslate", "(IFF)V", (void*) android_view_GLES20Canvas_translate },
+ { "nRotate", "(IF)V", (void*) android_view_GLES20Canvas_rotate },
+ { "nScale", "(IFF)V", (void*) android_view_GLES20Canvas_scale },
- { "nTranslate", "(IFF)V", (void*) android_view_GLES20Canvas_translate },
- { "nRotate", "(IF)V", (void*) android_view_GLES20Canvas_rotate },
- { "nScale", "(IFF)V", (void*) android_view_GLES20Canvas_scale },
+ { "nSetMatrix", "(II)V", (void*) android_view_GLES20Canvas_setMatrix },
+ { "nGetMatrix", "(II)V", (void*) android_view_GLES20Canvas_getMatrix },
+ { "nConcatMatrix", "(II)V", (void*) android_view_GLES20Canvas_concatMatrix },
- { "nSetMatrix", "(II)V", (void*) android_view_GLES20Canvas_setMatrix },
- { "nGetMatrix", "(II)V", (void*) android_view_GLES20Canvas_getMatrix },
- { "nConcatMatrix", "(II)V", (void*) android_view_GLES20Canvas_concatMatrix },
+ { "nDrawBitmap", "(IIFFI)V", (void*) android_view_GLES20Canvas_drawBitmap },
+ { "nDrawBitmap", "(IIFFFFFFFFI)V", (void*) android_view_GLES20Canvas_drawBitmapRect },
+ { "nDrawBitmap", "(IIII)V", (void*) android_view_GLES20Canvas_drawBitmapMatrix },
+ { "nDrawPatch", "(II[BFFFFI)V", (void*) android_view_GLES20Canvas_drawPatch },
+ { "nDrawColor", "(III)V", (void*) android_view_GLES20Canvas_drawColor },
+ { "nDrawRect", "(IFFFFI)V", (void*) android_view_GLES20Canvas_drawRect },
+ { "nDrawRects", "(III)V", (void*) android_view_GLES20Canvas_drawRects },
+ { "nDrawPath", "(III)V", (void*) android_view_GLES20Canvas_drawPath },
- { "nDrawBitmap", "(IIFFI)V", (void*) android_view_GLES20Canvas_drawBitmap },
- { "nDrawBitmap", "(IIFFFFFFFFI)V", (void*) android_view_GLES20Canvas_drawBitmapRect },
- { "nDrawBitmap", "(IIII)V", (void*) android_view_GLES20Canvas_drawBitmapMatrix },
- { "nDrawPatch", "(II[BFFFFI)V", (void*) android_view_GLES20Canvas_drawPatch },
- { "nDrawColor", "(III)V", (void*) android_view_GLES20Canvas_drawColor },
- { "nDrawRect", "(IFFFFI)V", (void*) android_view_GLES20Canvas_drawRect },
- { "nDrawPath", "(III)V", (void*) android_view_GLES20Canvas_drawPath },
+ { "nResetModifiers", "(I)V", (void*) android_view_GLES20Canvas_resetModifiers },
+ { "nSetupShader", "(II)V", (void*) android_view_GLES20Canvas_setupShader },
+ { "nSetupColorFilter", "(II)V", (void*) android_view_GLES20Canvas_setupColorFilter },
- { "nResetModifiers", "(I)V", (void*) android_view_GLES20Canvas_resetModifiers },
- { "nSetupShader", "(II)V", (void*) android_view_GLES20Canvas_setupShader },
- { "nSetupColorFilter", "(II)V", (void*) android_view_GLES20Canvas_setupColorFilter },
-
- { "nDrawText", "(I[CIIFFII)V", (void*) android_view_GLES20Canvas_drawTextArray },
- { "nDrawText", "(ILjava/lang/String;IIFFII)V",
+ { "nDrawText", "(I[CIIFFII)V", (void*) android_view_GLES20Canvas_drawTextArray },
+ { "nDrawText", "(ILjava/lang/String;IIFFII)V",
(void*) android_view_GLES20Canvas_drawText },
+ { "nDrawTextRun", "(I[CIIIIFFII)V", (void*) android_view_GLES20Canvas_drawTextRunArray },
+ { "nDrawTextRun", "(ILjava/lang/String;IIIIFFII)V",
+ (void*) android_view_GLES20Canvas_drawTextRun },
+
{ "nGetClipBounds", "(ILandroid/graphics/Rect;)Z",
(void*) android_view_GLES20Canvas_getClipBounds },
#endif
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 01ded68..1f66d05 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1284,6 +1284,9 @@
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true">
</activity>
+ <activity android:name="com.android.internal.app.PlatLogoActivity"
+ android:theme="@style/Theme.NoTitleBar.Fullscreen">
+ </activity>
<activity android:name="com.android.internal.app.DisableCarModeActivity"
android:theme="@style/Theme.NoDisplay"
android:excludeFromRecents="true">
diff --git a/core/res/res/drawable-nodpi/platlogo.jpg b/core/res/res/drawable-nodpi/platlogo.jpg
new file mode 100644
index 0000000..0e7780c
--- /dev/null
+++ b/core/res/res/drawable-nodpi/platlogo.jpg
Binary files differ
diff --git a/core/res/res/layout/action_mode_bar.xml b/core/res/res/layout/action_mode_bar.xml
index 8e2e69d..acf327e 100644
--- a/core/res/res/layout/action_mode_bar.xml
+++ b/core/res/res/layout/action_mode_bar.xml
@@ -19,4 +19,5 @@
<com.android.internal.widget.ActionBarContextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index f7f4d69..e768308 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -746,8 +746,8 @@
<string name="force_close" msgid="3653416315450806396">"Ukončit aplikaci"</string>
<string name="report" msgid="4060218260984795706">"Nahlásit"</string>
<string name="wait" msgid="7147118217226317732">"Počkat"</string>
- <string name="smv_application" msgid="295583804361236288">"Aplikace <xliff:g id="APPLICATION">%1$s</xliff:g> (proces<xliff:g id="PROCESS">%2$s</xliff:g>) porušila své samovynucované zásady StrictMode."</string>
- <string name="smv_process" msgid="5120397012047462446">"Proces <xliff:g id="PROCESS">%1$s</xliff:g> porušil své samovynucované zásady StrictMode."</string>
+ <string name="smv_application" msgid="295583804361236288">"Aplikace <xliff:g id="APPLICATION">%1$s</xliff:g> (proces <xliff:g id="PROCESS">%2$s</xliff:g>) porušila své vlastní vynucené zásady StrictMode."</string>
+ <string name="smv_process" msgid="5120397012047462446">"Proces <xliff:g id="PROCESS">%1$s</xliff:g> porušil své vlastní vynucené zásady StrictMode."</string>
<string name="heavy_weight_notification" msgid="9087063985776626166">"Běží aplikace <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Tuto možnost vyberte, chcete-li přepnout na aplikaci."</string>
<string name="heavy_weight_switcher_title" msgid="1135403633766694316">"Přepnout mezi aplikacemi?"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index ac42fd1..87376fe 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -749,7 +749,7 @@
<string name="smv_application" msgid="295583804361236288">"Die Anwendung <xliff:g id="APPLICATION">%1$s</xliff:g> (Prozess <xliff:g id="PROCESS">%2$s</xliff:g>) hat gegen ihre selbsterzwungene StrictMode-Richtlinie verstoßen."</string>
<string name="smv_process" msgid="5120397012047462446">"Der Prozess <xliff:g id="PROCESS">%1$s</xliff:g> hat gegen seine selbsterzwungene StrictMode-Richtlinie verstoßen."</string>
<string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> läuft"</string>
- <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Auswahl zum Wechseln in die Anwendung"</string>
+ <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Auswählen zum Wechseln in die Anwendung"</string>
<string name="heavy_weight_switcher_title" msgid="1135403633766694316">"Anwendung wechseln?"</string>
<string name="heavy_weight_switcher_text" msgid="4592075610079319667">"Eine andere Anwendung wird bereits ausgeführt und muss vor dem Start einer neuen Anwendung beendet werden."</string>
<string name="old_app_action" msgid="493129172238566282">"Zu <xliff:g id="OLD_APP">%1$s</xliff:g> zurückkehren"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index c2dd90b..04b6add 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -746,8 +746,8 @@
<string name="force_close" msgid="3653416315450806396">"Forcer la fermeture"</string>
<string name="report" msgid="4060218260984795706">"Rapport"</string>
<string name="wait" msgid="7147118217226317732">"Attendre"</string>
- <string name="smv_application" msgid="295583804361236288">"L\'application <xliff:g id="APPLICATION">%1$s</xliff:g> (processus <xliff:g id="PROCESS">%2$s</xliff:g>) a enfreint ses propres règles StrictMode."</string>
- <string name="smv_process" msgid="5120397012047462446">"Le processus <xliff:g id="PROCESS">%1$s</xliff:g> a enfreint ses propres règles StrictMode."</string>
+ <string name="smv_application" msgid="295583804361236288">"L\'application <xliff:g id="APPLICATION">%1$s</xliff:g> (processus <xliff:g id="PROCESS">%2$s</xliff:g>) a enfreint ses propres règles du mode strict."</string>
+ <string name="smv_process" msgid="5120397012047462446">"Le processus <xliff:g id="PROCESS">%1$s</xliff:g> a enfreint ses propres règles du mode strict."</string>
<string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> en cours d\'exécution"</string>
<string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Sélectionner pour changer d\'application"</string>
<string name="heavy_weight_switcher_title" msgid="1135403633766694316">"Passer d\'une application à l\'autre ?"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index fcb7eb7..21e8ad8 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -173,7 +173,7 @@
<string name="permlab_statusBar" msgid="7417192629601890791">"отключать или изменять строку состояния"</string>
<string name="permdesc_statusBar" msgid="1365473595331989732">"Позволяет приложению отключать строку состояния или добавлять/удалять системные значки."</string>
<string name="permlab_statusBarService" msgid="7247281911387931485">"строка состояния"</string>
- <string name="permdesc_statusBarService" msgid="4097605867643520920">"Позволяет приложению быть строкой состояния."</string>
+ <string name="permdesc_statusBarService" msgid="4097605867643520920">"Позволяет приложению заменять строку состояния."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"разворачивать/сворачивать строку состояния"</string>
<string name="permdesc_expandStatusBar" msgid="7088604400110768665">"Позволяет приложению разворачивать или сворачивать строку состояния."</string>
<string name="permlab_processOutgoingCalls" msgid="1136262550878335980">"перехватывать исходящие вызовы"</string>
@@ -315,7 +315,7 @@
<string name="permlab_recordAudio" msgid="3876049771427466323">"записывать аудио"</string>
<string name="permdesc_recordAudio" msgid="6493228261176552356">"Позволяет приложению получать доступ к пути аудиозаписи."</string>
<string name="permlab_camera" msgid="3616391919559751192">"снимать фото и видео"</string>
- <string name="permdesc_camera" msgid="6004878235852154239">"Позволяет приложению делать снимки и видео с помощью камеры. Это дает приложению возможность в любое время получать изображения с объектива камеры."</string>
+ <string name="permdesc_camera" msgid="6004878235852154239">"Позволяет приложению делать снимки и видео с помощью камеры в любое время."</string>
<string name="permlab_brick" msgid="8337817093326370537">"отключать телефон"</string>
<string name="permdesc_brick" msgid="5569526552607599221">"Позволяет данному приложению отключить телефон навсегда. Это очень опасно."</string>
<string name="permlab_reboot" msgid="2898560872462638242">"принудительно перезагружать телефон"</string>
@@ -749,7 +749,7 @@
<string name="smv_application" msgid="295583804361236288">"Приложение <xliff:g id="APPLICATION">%1$s</xliff:g> (процесс <xliff:g id="PROCESS">%2$s</xliff:g>) нарушило собственную политику StrictMode."</string>
<string name="smv_process" msgid="5120397012047462446">"Процесс <xliff:g id="PROCESS">%1$s</xliff:g> нарушил собственную политику StrictMode."</string>
<string name="heavy_weight_notification" msgid="9087063985776626166">"Приложение <xliff:g id="APP">%1$s</xliff:g> запущено"</string>
- <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Нажмите, чтобы переключиться в приложение"</string>
+ <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Нажмите, чтобы перейти к приложению"</string>
<string name="heavy_weight_switcher_title" msgid="1135403633766694316">"Переключить приложения?"</string>
<string name="heavy_weight_switcher_text" msgid="4592075610079319667">"Выполняется другое приложение, которое должно быть остановлено прежде, чем запускать новое."</string>
<string name="old_app_action" msgid="493129172238566282">"Вернуться к приложению <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 8e623e8..e8f5a20 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -563,7 +563,7 @@
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM-kortet är låst."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Låser upp SIM-kort…"</string>
<string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. "\n\n"Försök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"Du har angett ditt lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. "\n\n"Försök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. "\n\n"Försök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
<string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"Du har angett din PIN-kod fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. "\n\n"Försök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
<string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> försök till kommer du att uppmanas att låsa upp telefonen med din Google-inloggning."\n\n" Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Försök igen om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index b207db9..a9dd887 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -746,8 +746,8 @@
<string name="force_close" msgid="3653416315450806396">"Kapanmaya zorla"</string>
<string name="report" msgid="4060218260984795706">"Rapor"</string>
<string name="wait" msgid="7147118217226317732">"Bekle"</string>
- <string name="smv_application" msgid="295583804361236288">"<xliff:g id="APPLICATION">%1$s</xliff:g> uygulaması (<xliff:g id="PROCESS">%2$s</xliff:g> işlemi) kendiliğinden zorunlu StrictMode politikasını ihlal etti."</string>
- <string name="smv_process" msgid="5120397012047462446">"<xliff:g id="PROCESS">%1$s</xliff:g> işlemi kendiliğinden zorunlu StrictMode politikasını ihlal etti."</string>
+ <string name="smv_application" msgid="295583804361236288">"<xliff:g id="APPLICATION">%1$s</xliff:g> uygulaması (<xliff:g id="PROCESS">%2$s</xliff:g> işlemi) kendiliğinden uyguladığı StrictMode politikasını ihlal etti."</string>
+ <string name="smv_process" msgid="5120397012047462446">"<xliff:g id="PROCESS">%1$s</xliff:g> işlemi kendiliğinden uyguladığı StrictMode politikasını ihlal etti."</string>
<string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> çalışıyor"</string>
<string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Uygulama değiştirmeyi seçin"</string>
<string name="heavy_weight_switcher_title" msgid="1135403633766694316">"Uygulamaların arasında geçiş yapılsın mı?"</string>
diff --git a/core/res/res/values-xlarge/styles.xml b/core/res/res/values-xlarge/styles.xml
index 40e423e..ff7df7c 100644
--- a/core/res/res/values-xlarge/styles.xml
+++ b/core/res/res/values-xlarge/styles.xml
@@ -19,7 +19,6 @@
<style name="TextAppearance.StatusBar">
<item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
- <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
</style>
<style name="TextAppearance.StatusBar.Ticker">
</style>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 4d78b86..0949553 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -315,7 +315,7 @@
<string name="permlab_recordAudio" msgid="3876049771427466323">"录音"</string>
<string name="permdesc_recordAudio" msgid="6493228261176552356">"允许应用程序访问录音路径。"</string>
<string name="permlab_camera" msgid="3616391919559751192">"拍摄照片和视频"</string>
- <string name="permdesc_camera" msgid="6004878235852154239">"允许应用程序使用相机拍摄照片和视频,这样应用程序可随时收集进入相机镜头中看到的图片。"</string>
+ <string name="permdesc_camera" msgid="6004878235852154239">"允许应用程序使用相机拍摄照片和视频,这样应用程序可随时收集进入相机镜头中的图片。"</string>
<string name="permlab_brick" msgid="8337817093326370537">"永久停用手机"</string>
<string name="permdesc_brick" msgid="5569526552607599221">"允许应用程序永久停用整个手机,这非常危险。"</string>
<string name="permlab_reboot" msgid="2898560872462638242">"强行重新启动手机"</string>
@@ -747,7 +747,7 @@
<string name="report" msgid="4060218260984795706">"报告"</string>
<string name="wait" msgid="7147118217226317732">"等待"</string>
<string name="smv_application" msgid="295583804361236288">"应用程序<xliff:g id="APPLICATION">%1$s</xliff:g>(<xliff:g id="PROCESS">%2$s</xliff:g> 进程)违反了自我强制执行的严格模式 (StrictMode) 政策。"</string>
- <string name="smv_process" msgid="5120397012047462446">"进程 <xliff:g id="PROCESS">%1$s</xliff:g> 违反了自我强制执行的严格模式 (StrictMode) 政策"</string>
+ <string name="smv_process" msgid="5120397012047462446">"进程 <xliff:g id="PROCESS">%1$s</xliff:g> 违反了自我强制执行的严格模式 (StrictMode) 政策。"</string>
<string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g>正在运行"</string>
<string name="heavy_weight_notification_detail" msgid="2423977499339403402">"选择以切换到该应用程序"</string>
<string name="heavy_weight_switcher_title" msgid="1135403633766694316">"要切换应用程序吗?"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 4ab9907..2f0a49b 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -34,7 +34,7 @@
<string name="defaultVoiceMailAlphaTag" msgid="2660020990097733077">"語音留言"</string>
<string name="defaultMsisdnAlphaTag" msgid="2850889754919584674">"MSISDN1"</string>
<string name="mmiError" msgid="5154499457739052907">"連線發生問題或錯誤的 MMI 碼。"</string>
- <string name="mmiFdnError" msgid="5224398216385316471">"僅允許在固定撥號時使用此操作。"</string>
+ <string name="mmiFdnError" msgid="5224398216385316471">"僅限對固定撥號號碼執行此作業。"</string>
<string name="serviceEnabled" msgid="8147278346414714315">"服務已啟用。"</string>
<string name="serviceEnabledFor" msgid="6856228140453471041">"已啟用服務:"</string>
<string name="serviceDisabled" msgid="1937553226592516411">"服務已停用。"</string>
@@ -173,7 +173,7 @@
<string name="permlab_statusBar" msgid="7417192629601890791">"停用或變更狀態列"</string>
<string name="permdesc_statusBar" msgid="1365473595331989732">"允許應用程式停用狀態列或新增、移除系統圖示。"</string>
<string name="permlab_statusBarService" msgid="7247281911387931485">"狀態列"</string>
- <string name="permdesc_statusBarService" msgid="4097605867643520920">"允許應用程式成為狀態列。"</string>
+ <string name="permdesc_statusBarService" msgid="4097605867643520920">"允許應用程式以狀態列顯示。"</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"展開/收攏狀態列"</string>
<string name="permdesc_expandStatusBar" msgid="7088604400110768665">"允許應用程式展開或收攏狀態列。"</string>
<string name="permlab_processOutgoingCalls" msgid="1136262550878335980">"攔截撥出電話"</string>
@@ -314,8 +314,8 @@
<string name="permdesc_modifyAudioSettings" msgid="5793461287365991922">"允許應用程式編輯全域音訊設定,例如音量與路由。"</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"錄製音訊"</string>
<string name="permdesc_recordAudio" msgid="6493228261176552356">"允許應用程式存取音訊錄製路徑。"</string>
- <string name="permlab_camera" msgid="3616391919559751192">"拍照和錄影"</string>
- <string name="permdesc_camera" msgid="6004878235852154239">"允許應用程式使用相機拍照和錄影。此功能可讓應用程式隨時透過相機收集圖片。"</string>
+ <string name="permlab_camera" msgid="3616391919559751192">"拍照和拍攝影片"</string>
+ <string name="permdesc_camera" msgid="6004878235852154239">"允許應用程式使用相機拍照和錄影,此功能可讓應用程式隨時透過相機收集圖片。"</string>
<string name="permlab_brick" msgid="8337817093326370537">"永久停用電話"</string>
<string name="permdesc_brick" msgid="5569526552607599221">"允許應用程式永久停用手機。此項操作非常危險。"</string>
<string name="permlab_reboot" msgid="2898560872462638242">"強制重開機"</string>
@@ -535,7 +535,7 @@
<string name="orgTypeCustom" msgid="225523415372088322">"自訂"</string>
<string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"輸入 PIN 碼"</string>
<string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"輸入密碼即可解鎖"</string>
- <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"輸入 PIN 即可解鎖"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"輸入 PIN 進行解鎖"</string>
<string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"PIN 碼錯誤!"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"如要解鎖,請按 Menu 鍵,然後按 0。"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"緊急電話號碼"</string>
@@ -563,8 +563,8 @@
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM 卡已鎖定。"</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"解鎖 SIM 卡中..."</string>
<string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"畫出解鎖圖形已錯誤 <xliff:g id="NUMBER_0">%d</xliff:g> 次。"\n\n" 請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再嘗試。"</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"您已輸入 <xliff:g id="NUMBER_0">%d</xliff:g> 次不正確的密碼。"\n\n"請於 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"您已輸入 <xliff:g id="NUMBER_0">%d</xliff:g> 次不正確的 PIN。"\n\n"請於 <xliff:g id="NUMBER_1">%d</xliff:g> 秒內再試一次。"</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"您已 <xliff:g id="NUMBER_0">%d</xliff:g> 次輸入不正確的密碼。"\n\n"請於 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"您已 <xliff:g id="NUMBER_0">%d</xliff:g> 次輸入不正確的 PIN。"\n\n"請於 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
<string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"畫出解鎖圖形已錯誤 <xliff:g id="NUMBER_0">%d</xliff:g> 次。再錯誤 <xliff:g id="NUMBER_1">%d</xliff:g> 次後,系統會要求使用 Google 登入來解鎖。"\n\n" 請在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒後再試一次。"</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"<xliff:g id="NUMBER">%d</xliff:g> 秒後再試一次。"</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"忘記解鎖圖形?"</string>
@@ -746,12 +746,12 @@
<string name="force_close" msgid="3653416315450806396">"強制關閉"</string>
<string name="report" msgid="4060218260984795706">"回報"</string>
<string name="wait" msgid="7147118217226317732">"等待"</string>
- <string name="smv_application" msgid="295583804361236288">"應用程式 <xliff:g id="APPLICATION">%1$s</xliff:g> (處理程序 <xliff:g id="PROCESS">%2$s</xliff:g>) 已違反其自行實施的 StrictMode 政策。"</string>
- <string name="smv_process" msgid="5120397012047462446">"處理程序 <xliff:g id="PROCESS">%1$s</xliff:g> 已違反其自行實施的 StrictMode 政策。"</string>
+ <string name="smv_application" msgid="295583804361236288">"應用程式 <xliff:g id="APPLICATION">%1$s</xliff:g> (處理程序 <xliff:g id="PROCESS">%2$s</xliff:g>) 已違反其自行強制實施的嚴格模式 (StrictMode) 政策。"</string>
+ <string name="smv_process" msgid="5120397012047462446">"處理程序 <xliff:g id="PROCESS">%1$s</xliff:g> 已違反其自行強制實施的嚴格模式 (StrictMode) 政策。"</string>
<string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> 執行中"</string>
- <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"選取切換應用程式"</string>
+ <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"選取以切換到該應用程式"</string>
<string name="heavy_weight_switcher_title" msgid="1135403633766694316">"切換應用程式?"</string>
- <string name="heavy_weight_switcher_text" msgid="4592075610079319667">"其他應用程式已在執行中,您必須停止執行,才能啟動新的應用程式。"</string>
+ <string name="heavy_weight_switcher_text" msgid="4592075610079319667">"其他應用程式已在執行中,您必須停止執行該應用程式,才能啟動新的應用程式。"</string>
<string name="old_app_action" msgid="493129172238566282">"返回 <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
<string name="old_app_description" msgid="942967900237208466">"請勿啟動新的應用程式。"</string>
<string name="new_app_action" msgid="5472756926945440706">"啟動 <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
@@ -862,15 +862,15 @@
<string name="l2tp_ipsec_psk_vpn_description" msgid="3945043564008303239">"採用預先共用金鑰的 L2TP/IPSec VPN"</string>
<string name="l2tp_ipsec_crt_vpn_description" msgid="5382714073103653577">"採用憑證的 L2TP/IPSec VPN"</string>
<string name="upload_file" msgid="2897957172366730416">"選擇檔案"</string>
- <string name="no_file_chosen" msgid="6363648562170759465">"沒有選擇檔案"</string>
+ <string name="no_file_chosen" msgid="6363648562170759465">"未選擇任何檔案"</string>
<string name="reset" msgid="2448168080964209908">"重設"</string>
<string name="submit" msgid="1602335572089911941">"提交"</string>
<string name="car_mode_disable_notification_title" msgid="3164768212003864316">"已啟用車用模式"</string>
<string name="car_mode_disable_notification_message" msgid="668663626721675614">"選取結束車用模式。"</string>
<string name="tethered_notification_title" msgid="3146694234398202601">"數據連線或無線基地台已啟用"</string>
<string name="tethered_notification_message" msgid="3067108323903048927">"輕觸以設定"</string>
- <string name="back_button_label" msgid="2300470004503343439">"上一個"</string>
- <string name="next_button_label" msgid="1080555104677992408">"下一個"</string>
+ <string name="back_button_label" msgid="2300470004503343439">"上一頁"</string>
+ <string name="next_button_label" msgid="1080555104677992408">"下一頁"</string>
<string name="throttle_warning_notification_title" msgid="4890894267454867276">"高行動資料用量"</string>
<string name="throttle_warning_notification_message" msgid="2609734763845705708">"輕觸即可瞭解更多有關行動資料用量的詳細資訊"</string>
<string name="throttled_notification_title" msgid="6269541897729781332">"已達行動資料上限"</string>
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
index 0fe83e1..cbd8714 100644
--- a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
+++ b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
@@ -17,13 +17,11 @@
package android.bluetooth;
import android.app.Instrumentation;
-import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
public class BluetoothStressTest extends InstrumentationTestCase {
@@ -161,7 +159,6 @@
mContext.unregisterReceiver(mReceiver);
}
- @LargeTest
public void testEnableDisable() {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
@@ -172,7 +169,6 @@
}
}
- @LargeTest
public void testDiscoverable() {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
enable(adapter);
@@ -186,7 +182,6 @@
disable(adapter);
}
- @LargeTest
public void testScan() {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
enable(adapter);
@@ -336,7 +331,7 @@
mReceiver.resetFiredFlags();
if (!adapter.isEnabled()) {
- fail("undiscoverable(): bluetooth not enabled");
+ fail("undiscoverable() bluetooth not enabled");
}
int scanMode = adapter.getScanMode();
@@ -374,7 +369,7 @@
mReceiver.resetFiredFlags();
if (!adapter.isEnabled()) {
- fail("startScan(): bluetooth not enabled");
+ fail("startScan() bluetooth not enabled");
}
if (adapter.isDiscovering()) {
@@ -404,7 +399,7 @@
mReceiver.resetFiredFlags();
if (!adapter.isEnabled()) {
- fail("stopScan(): bluetooth not enabled");
+ fail("stopScan() bluetooth not enabled");
}
if (!adapter.isDiscovering()) {
diff --git a/graphics/java/android/graphics/ComposeShader.java b/graphics/java/android/graphics/ComposeShader.java
index ac1f277..e88211a 100644
--- a/graphics/java/android/graphics/ComposeShader.java
+++ b/graphics/java/android/graphics/ComposeShader.java
@@ -20,7 +20,12 @@
an {@link android.graphics.Xfermode} subclass.
*/
public class ComposeShader extends Shader {
+ /**
+ * Hold onto the shaders to avoid GC.
+ */
+ @SuppressWarnings({"UnusedDeclaration"})
private final Shader mShaderA;
+ @SuppressWarnings({"UnusedDeclaration"})
private final Shader mShaderB;
/** Create a new compose shader, given shaders A, B, and a combining mode.
@@ -36,8 +41,14 @@
mShaderB = shaderB;
native_instance = nativeCreate1(shaderA.native_instance, shaderB.native_instance,
(mode != null) ? mode.native_instance : 0);
- native_shader = nativePostCreate1(native_instance, shaderA.native_shader,
- shaderB.native_shader, (mode != null) ? mode.native_instance : 0);
+ if (mode instanceof PorterDuffXfermode) {
+ PorterDuff.Mode pdMode = ((PorterDuffXfermode) mode).mode;
+ native_shader = nativePostCreate1(native_instance, shaderA.native_shader,
+ shaderB.native_shader, pdMode != null ? pdMode.nativeInt : 0);
+ } else {
+ native_shader = nativePostCreate1(native_instance, shaderA.native_shader,
+ shaderB.native_shader, mode != null ? mode.native_instance : 0);
+ }
}
/** Create a new compose shader, given shaders A, B, and a combining PorterDuff mode.
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index cb2c6a2..c3416a0 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -16,6 +16,8 @@
package android.graphics;
+import android.view.HardwareRenderer;
+
/**
* The Path class encapsulates compound (multiple contour) geometric paths
* consisting of straight line segments, quadratic curves, and cubic curves.
@@ -24,12 +26,27 @@
* text on a path.
*/
public class Path {
+ /**
+ * @hide
+ */
+ public final int mNativePath;
+
+ /**
+ * @hide
+ */
+ public boolean isSimplePath = true;
+ /**
+ * @hide
+ */
+ public Region rects;
+ private boolean mDetectSimplePaths;
/**
* Create an empty path
*/
public Path() {
mNativePath = init1();
+ mDetectSimplePaths = HardwareRenderer.isAvailable();
}
/**
@@ -43,6 +60,7 @@
valNative = src.mNativePath;
}
mNativePath = init2(valNative);
+ mDetectSimplePaths = HardwareRenderer.isAvailable();
}
/**
@@ -50,6 +68,10 @@
* This does NOT change the fill-type setting.
*/
public void reset() {
+ isSimplePath = true;
+ if (mDetectSimplePaths) {
+ if (rects != null) rects.setEmpty();
+ }
native_reset(mNativePath);
}
@@ -58,6 +80,10 @@
* keeps the internal data structure for faster reuse.
*/
public void rewind() {
+ isSimplePath = true;
+ if (mDetectSimplePaths) {
+ if (rects != null) rects.setEmpty();
+ }
native_rewind(mNativePath);
}
@@ -65,6 +91,7 @@
*/
public void set(Path src) {
if (this != src) {
+ isSimplePath = src.isSimplePath;
native_set(mNativePath, src.mNativePath);
}
}
@@ -160,6 +187,7 @@
* @param bounds Returns the computed bounds of the path's control points.
* @param exact This parameter is no longer used.
*/
+ @SuppressWarnings({"UnusedDeclaration"})
public void computeBounds(RectF bounds, boolean exact) {
native_computeBounds(mNativePath, bounds);
}
@@ -236,6 +264,7 @@
* @param y2 The y-coordinate of the end point on a quadratic curve
*/
public void quadTo(float x1, float y1, float x2, float y2) {
+ isSimplePath = false;
native_quadTo(mNativePath, x1, y1, x2, y2);
}
@@ -254,6 +283,7 @@
* this contour, for the end point of a quadratic curve
*/
public void rQuadTo(float dx1, float dy1, float dx2, float dy2) {
+ isSimplePath = false;
native_rQuadTo(mNativePath, dx1, dy1, dx2, dy2);
}
@@ -271,6 +301,7 @@
*/
public void cubicTo(float x1, float y1, float x2, float y2,
float x3, float y3) {
+ isSimplePath = false;
native_cubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
}
@@ -281,6 +312,7 @@
*/
public void rCubicTo(float x1, float y1, float x2, float y2,
float x3, float y3) {
+ isSimplePath = false;
native_rCubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
}
@@ -299,6 +331,7 @@
*/
public void arcTo(RectF oval, float startAngle, float sweepAngle,
boolean forceMoveTo) {
+ isSimplePath = false;
native_arcTo(mNativePath, oval, startAngle, sweepAngle, forceMoveTo);
}
@@ -314,6 +347,7 @@
* @param sweepAngle Sweep angle (in degrees) measured clockwise
*/
public void arcTo(RectF oval, float startAngle, float sweepAngle) {
+ isSimplePath = false;
native_arcTo(mNativePath, oval, startAngle, sweepAngle, false);
}
@@ -322,6 +356,7 @@
* first point of the contour, a line segment is automatically added.
*/
public void close() {
+ isSimplePath = false;
native_close(mNativePath);
}
@@ -351,6 +386,11 @@
if (rect == null) {
throw new NullPointerException("need rect parameter");
}
+ if (mDetectSimplePaths) {
+ if (rects == null) rects = new Region();
+ rects.op((int) rect.left, (int) rect.top, (int) rect.right, (int) rect.bottom,
+ Region.Op.UNION);
+ }
native_addRect(mNativePath, rect, dir.nativeInt);
}
@@ -363,8 +403,11 @@
* @param bottom The bottom of a rectangle to add to the path
* @param dir The direction to wind the rectangle's contour
*/
- public void addRect(float left, float top, float right, float bottom,
- Direction dir) {
+ public void addRect(float left, float top, float right, float bottom, Direction dir) {
+ if (mDetectSimplePaths) {
+ if (rects == null) rects = new Region();
+ rects.op((int) left, (int) top, (int) right, (int) bottom, Region.Op.UNION);
+ }
native_addRect(mNativePath, left, top, right, bottom, dir.nativeInt);
}
@@ -378,6 +421,7 @@
if (oval == null) {
throw new NullPointerException("need oval parameter");
}
+ isSimplePath = false;
native_addOval(mNativePath, oval, dir.nativeInt);
}
@@ -390,6 +434,7 @@
* @param dir The direction to wind the circle's contour
*/
public void addCircle(float x, float y, float radius, Direction dir) {
+ isSimplePath = false;
native_addCircle(mNativePath, x, y, radius, dir.nativeInt);
}
@@ -404,6 +449,7 @@
if (oval == null) {
throw new NullPointerException("need oval parameter");
}
+ isSimplePath = false;
native_addArc(mNativePath, oval, startAngle, sweepAngle);
}
@@ -419,6 +465,7 @@
if (rect == null) {
throw new NullPointerException("need rect parameter");
}
+ isSimplePath = false;
native_addRoundRect(mNativePath, rect, rx, ry, dir.nativeInt);
}
@@ -438,6 +485,7 @@
if (radii.length < 8) {
throw new ArrayIndexOutOfBoundsException("radii[] needs 8 values");
}
+ isSimplePath = false;
native_addRoundRect(mNativePath, rect, radii, dir.nativeInt);
}
@@ -448,6 +496,7 @@
* @param dx The amount to translate the path in X as it is added
*/
public void addPath(Path src, float dx, float dy) {
+ isSimplePath = false;
native_addPath(mNativePath, src.mNativePath, dx, dy);
}
@@ -457,6 +506,7 @@
* @param src The path that is appended to the current path
*/
public void addPath(Path src) {
+ isSimplePath = false;
native_addPath(mNativePath, src.mNativePath);
}
@@ -466,6 +516,7 @@
* @param src The path to add as a new contour
*/
public void addPath(Path src, Matrix matrix) {
+ if (!src.isSimplePath) isSimplePath = false;
native_addPath(mNativePath, src.mNativePath, matrix.native_instance);
}
@@ -502,6 +553,7 @@
* @param dy The new Y coordinate for the last point
*/
public void setLastPoint(float dx, float dy) {
+ isSimplePath = false;
native_setLastPoint(mNativePath, dx, dy);
}
@@ -537,8 +589,8 @@
super.finalize();
}
}
-
- /*package*/ final int ni() {
+
+ final int ni() {
return mNativePath;
}
@@ -592,9 +644,4 @@
int dst_path);
private static native void native_transform(int nPath, int matrix);
private static native void finalizer(int nPath);
-
- /**
- * @hide
- */
- public final int mNativePath;
}
diff --git a/graphics/java/android/graphics/PorterDuffXfermode.java b/graphics/java/android/graphics/PorterDuffXfermode.java
index cb127fd..6ba064c 100644
--- a/graphics/java/android/graphics/PorterDuffXfermode.java
+++ b/graphics/java/android/graphics/PorterDuffXfermode.java
@@ -18,11 +18,17 @@
public class PorterDuffXfermode extends Xfermode {
/**
+ * @hide
+ */
+ public final PorterDuff.Mode mode;
+
+ /**
* Create an xfermode that uses the specified porter-duff mode.
*
* @param mode The porter-duff mode that is applied
*/
public PorterDuffXfermode(PorterDuff.Mode mode) {
+ this.mode = mode;
native_instance = nativeCreateXfermode(mode.nativeInt);
}
diff --git a/graphics/java/android/graphics/Region.java b/graphics/java/android/graphics/Region.java
index 489ef83..e540806 100644
--- a/graphics/java/android/graphics/Region.java
+++ b/graphics/java/android/graphics/Region.java
@@ -20,6 +20,10 @@
import android.os.Parcelable;
public class Region implements Parcelable {
+ /**
+ * @hide
+ */
+ public final int mNativeRegion;
// the native values for these must match up with the enum in SkRegion.h
public enum Op {
@@ -329,10 +333,14 @@
}
protected void finalize() throws Throwable {
- nativeDestructor(mNativeRegion);
+ try {
+ nativeDestructor(mNativeRegion);
+ } finally {
+ super.finalize();
+ }
}
- /*package*/ Region(int ni) {
+ Region(int ni) {
if (ni == 0) {
throw new RuntimeException();
}
@@ -345,7 +353,7 @@
this(ni);
}
- /*package*/ final int ni() {
+ final int ni() {
return mNativeRegion;
}
@@ -374,6 +382,4 @@
Parcel p);
private static native boolean nativeEquals(int native_r1, int native_r2);
-
- private final int mNativeRegion;
}
diff --git a/graphics/java/android/graphics/Xfermode.java b/graphics/java/android/graphics/Xfermode.java
index 42c410e..2467bdc 100644
--- a/graphics/java/android/graphics/Xfermode.java
+++ b/graphics/java/android/graphics/Xfermode.java
@@ -31,7 +31,11 @@
public class Xfermode {
protected void finalize() throws Throwable {
- finalizer(native_instance);
+ try {
+ finalizer(native_instance);
+ } finally {
+ super.finalize();
+ }
}
private static native void finalizer(int native_instance);
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 33ecbea..88f6d43 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -313,18 +313,16 @@
case RECTANGLE:
if (st.mRadiusArray != null) {
mPath.reset();
- mPath.addRoundRect(mRect, st.mRadiusArray,
- Path.Direction.CW);
+ mPath.addRoundRect(mRect, st.mRadiusArray, Path.Direction.CW);
canvas.drawPath(mPath, mFillPaint);
if (haveStroke) {
canvas.drawPath(mPath, mStrokePaint);
}
- }
- else {
+ } else if (st.mRadius > 0.0f) {
// since the caller is only giving us 1 value, we will force
// it to be square if the rect is too small in one dimension
// to show it. If we did nothing, Skia would clamp the rad
- // independently along each axis, giving us a thin ellips
+ // independently along each axis, giving us a thin ellipse
// if the rect were very wide but not very tall
float rad = st.mRadius;
float r = Math.min(mRect.width(), mRect.height()) * 0.5f;
@@ -335,6 +333,11 @@
if (haveStroke) {
canvas.drawRoundRect(mRect, rad, rad, mStrokePaint);
}
+ } else {
+ canvas.drawRect(mRect, mFillPaint);
+ if (haveStroke) {
+ canvas.drawRect(mRect, mStrokePaint);
+ }
}
break;
case OVAL:
diff --git a/include/android_runtime/android_content_res_Configuration.h b/include/android_runtime/android_content_res_Configuration.h
new file mode 100644
index 0000000..2f5a982
--- /dev/null
+++ b/include/android_runtime/android_content_res_Configuration.h
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_CONTENT_RES_CONFIGURATION_H
+#define _ANDROID_CONTENT_RES_CONFIGURATION_H
+
+#include <utils/ResourceTypes.h>
+#include <android/configuration.h>
+
+#include "jni.h"
+
+struct AConfiguration : android::ResTable_config {
+};
+
+namespace android {
+
+extern void android_Configuration_getFromJava(
+ JNIEnv* env, jobject clazz, struct AConfiguration* out);
+
+} // namespace android
+
+
+#endif // _ANDROID_CONTENT_RES_CONFIGURATION_H
diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h
index 9cddab2..92ce068 100644
--- a/include/media/stagefright/MediaDefs.h
+++ b/include/media/stagefright/MediaDefs.h
@@ -34,6 +34,8 @@
extern const char *MEDIA_MIMETYPE_AUDIO_AAC;
extern const char *MEDIA_MIMETYPE_AUDIO_QCELP;
extern const char *MEDIA_MIMETYPE_AUDIO_VORBIS;
+extern const char *MEDIA_MIMETYPE_AUDIO_G711_ALAW;
+extern const char *MEDIA_MIMETYPE_AUDIO_G711_MLAW;
extern const char *MEDIA_MIMETYPE_AUDIO_RAW;
extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4;
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index f162231..71c6c51 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -454,6 +454,8 @@
virtual void reset();
virtual void process(const RawEvent* rawEvent);
+ virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
+
private:
// Amount that trackball needs to move in order to generate a key event.
static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6;
diff --git a/include/utils/AssetManager.h b/include/utils/AssetManager.h
index 97694ff..9e2bf37 100644
--- a/include/utils/AssetManager.h
+++ b/include/utils/AssetManager.h
@@ -129,6 +129,8 @@
*/
void setConfiguration(const ResTable_config& config, const char* locale = NULL);
+ void getConfiguration(ResTable_config* outConfig) const;
+
typedef Asset::AccessMode AccessMode; // typing shortcut
/*
diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h
index c7d9ff1..da86da4 100644
--- a/include/utils/ResourceTypes.h
+++ b/include/utils/ResourceTypes.h
@@ -31,6 +31,8 @@
#include <stdint.h>
#include <sys/types.h>
+#include <android/configuration.h>
+
namespace android {
/** ********************************************************************
@@ -822,25 +824,25 @@
};
enum {
- ORIENTATION_ANY = 0x0000,
- ORIENTATION_PORT = 0x0001,
- ORIENTATION_LAND = 0x0002,
- ORIENTATION_SQUARE = 0x0003,
+ ORIENTATION_ANY = ACONFIGURATION_ORIENTATION_ANY,
+ ORIENTATION_PORT = ACONFIGURATION_ORIENTATION_PORT,
+ ORIENTATION_LAND = ACONFIGURATION_ORIENTATION_LAND,
+ ORIENTATION_SQUARE = ACONFIGURATION_ORIENTATION_SQUARE,
};
enum {
- TOUCHSCREEN_ANY = 0x0000,
- TOUCHSCREEN_NOTOUCH = 0x0001,
- TOUCHSCREEN_STYLUS = 0x0002,
- TOUCHSCREEN_FINGER = 0x0003,
+ TOUCHSCREEN_ANY = ACONFIGURATION_TOUCHSCREEN_ANY,
+ TOUCHSCREEN_NOTOUCH = ACONFIGURATION_TOUCHSCREEN_NOTOUCH,
+ TOUCHSCREEN_STYLUS = ACONFIGURATION_TOUCHSCREEN_STYLUS,
+ TOUCHSCREEN_FINGER = ACONFIGURATION_TOUCHSCREEN_FINGER,
};
enum {
- DENSITY_DEFAULT = 0,
- DENSITY_LOW = 120,
- DENSITY_MEDIUM = 160,
- DENSITY_HIGH = 240,
- DENSITY_NONE = 0xffff
+ DENSITY_DEFAULT = ACONFIGURATION_DENSITY_DEFAULT,
+ DENSITY_LOW = ACONFIGURATION_DENSITY_LOW,
+ DENSITY_MEDIUM = ACONFIGURATION_DENSITY_MEDIUM,
+ DENSITY_HIGH = ACONFIGURATION_DENSITY_HIGH,
+ DENSITY_NONE = ACONFIGURATION_DENSITY_NONE
};
union {
@@ -853,33 +855,34 @@
};
enum {
- KEYBOARD_ANY = 0x0000,
- KEYBOARD_NOKEYS = 0x0001,
- KEYBOARD_QWERTY = 0x0002,
- KEYBOARD_12KEY = 0x0003,
+ KEYBOARD_ANY = ACONFIGURATION_KEYBOARD_ANY,
+ KEYBOARD_NOKEYS = ACONFIGURATION_KEYBOARD_NOKEYS,
+ KEYBOARD_QWERTY = ACONFIGURATION_KEYBOARD_QWERTY,
+ KEYBOARD_12KEY = ACONFIGURATION_KEYBOARD_12KEY,
};
enum {
- NAVIGATION_ANY = 0x0000,
- NAVIGATION_NONAV = 0x0001,
- NAVIGATION_DPAD = 0x0002,
- NAVIGATION_TRACKBALL = 0x0003,
- NAVIGATION_WHEEL = 0x0004,
+ NAVIGATION_ANY = ACONFIGURATION_NAVIGATION_ANY,
+ NAVIGATION_NONAV = ACONFIGURATION_NAVIGATION_NONAV,
+ NAVIGATION_DPAD = ACONFIGURATION_NAVIGATION_DPAD,
+ NAVIGATION_TRACKBALL = ACONFIGURATION_NAVIGATION_TRACKBALL,
+ NAVIGATION_WHEEL = ACONFIGURATION_NAVIGATION_WHEEL,
};
enum {
MASK_KEYSHIDDEN = 0x0003,
- KEYSHIDDEN_ANY = 0x0000,
- KEYSHIDDEN_NO = 0x0001,
- KEYSHIDDEN_YES = 0x0002,
- KEYSHIDDEN_SOFT = 0x0003,
+ KEYSHIDDEN_ANY = ACONFIGURATION_KEYSHIDDEN_ANY,
+ KEYSHIDDEN_NO = ACONFIGURATION_KEYSHIDDEN_NO,
+ KEYSHIDDEN_YES = ACONFIGURATION_KEYSHIDDEN_YES,
+ KEYSHIDDEN_SOFT = ACONFIGURATION_KEYSHIDDEN_SOFT,
};
enum {
MASK_NAVHIDDEN = 0x000c,
- NAVHIDDEN_ANY = 0x0000,
- NAVHIDDEN_NO = 0x0004,
- NAVHIDDEN_YES = 0x0008,
+ SHIFT_NAVHIDDEN = 2,
+ NAVHIDDEN_ANY = ACONFIGURATION_NAVHIDDEN_ANY << SHIFT_NAVHIDDEN,
+ NAVHIDDEN_NO = ACONFIGURATION_NAVHIDDEN_NO << SHIFT_NAVHIDDEN,
+ NAVHIDDEN_YES = ACONFIGURATION_NAVHIDDEN_YES << SHIFT_NAVHIDDEN,
};
union {
@@ -929,32 +932,34 @@
enum {
// screenLayout bits for screen size class.
MASK_SCREENSIZE = 0x0f,
- SCREENSIZE_ANY = 0x00,
- SCREENSIZE_SMALL = 0x01,
- SCREENSIZE_NORMAL = 0x02,
- SCREENSIZE_LARGE = 0x03,
- SCREENSIZE_XLARGE = 0x04,
+ SCREENSIZE_ANY = ACONFIGURATION_SCREENSIZE_ANY,
+ SCREENSIZE_SMALL = ACONFIGURATION_SCREENSIZE_SMALL,
+ SCREENSIZE_NORMAL = ACONFIGURATION_SCREENSIZE_NORMAL,
+ SCREENSIZE_LARGE = ACONFIGURATION_SCREENSIZE_LARGE,
+ SCREENSIZE_XLARGE = ACONFIGURATION_SCREENSIZE_XLARGE,
// screenLayout bits for wide/long screen variation.
MASK_SCREENLONG = 0x30,
- SCREENLONG_ANY = 0x00,
- SCREENLONG_NO = 0x10,
- SCREENLONG_YES = 0x20,
+ SHIFT_SCREENLONG = 4,
+ SCREENLONG_ANY = ACONFIGURATION_SCREENLONG_ANY << SHIFT_SCREENLONG,
+ SCREENLONG_NO = ACONFIGURATION_SCREENLONG_NO << SHIFT_SCREENLONG,
+ SCREENLONG_YES = ACONFIGURATION_SCREENLONG_YES << SHIFT_SCREENLONG,
};
enum {
// uiMode bits for the mode type.
MASK_UI_MODE_TYPE = 0x0f,
- UI_MODE_TYPE_ANY = 0x00,
- UI_MODE_TYPE_NORMAL = 0x01,
- UI_MODE_TYPE_DESK = 0x02,
- UI_MODE_TYPE_CAR = 0x03,
+ UI_MODE_TYPE_ANY = ACONFIGURATION_UI_MODE_TYPE_ANY,
+ UI_MODE_TYPE_NORMAL = ACONFIGURATION_UI_MODE_TYPE_NORMAL,
+ UI_MODE_TYPE_DESK = ACONFIGURATION_UI_MODE_TYPE_DESK,
+ UI_MODE_TYPE_CAR = ACONFIGURATION_UI_MODE_TYPE_CAR,
// uiMode bits for the night switch.
MASK_UI_MODE_NIGHT = 0x30,
- UI_MODE_NIGHT_ANY = 0x00,
- UI_MODE_NIGHT_NO = 0x10,
- UI_MODE_NIGHT_YES = 0x20,
+ SHIFT_UI_MODE_NIGHT = 4,
+ UI_MODE_NIGHT_ANY = ACONFIGURATION_UI_MODE_NIGHT_ANY << SHIFT_UI_MODE_NIGHT,
+ UI_MODE_NIGHT_NO = ACONFIGURATION_UI_MODE_NIGHT_NO << SHIFT_UI_MODE_NIGHT,
+ UI_MODE_NIGHT_YES = ACONFIGURATION_UI_MODE_NIGHT_YES << SHIFT_UI_MODE_NIGHT,
};
union {
@@ -1023,19 +1028,19 @@
// match the corresponding ones in android.content.pm.ActivityInfo and
// attrs_manifest.xml.
enum {
- CONFIG_MCC = 0x0001,
- CONFIG_MNC = 0x0002,
- CONFIG_LOCALE = 0x0004,
- CONFIG_TOUCHSCREEN = 0x0008,
- CONFIG_KEYBOARD = 0x0010,
- CONFIG_KEYBOARD_HIDDEN = 0x0020,
- CONFIG_NAVIGATION = 0x0040,
- CONFIG_ORIENTATION = 0x0080,
- CONFIG_DENSITY = 0x0100,
- CONFIG_SCREEN_SIZE = 0x0200,
- CONFIG_VERSION = 0x0400,
- CONFIG_SCREEN_LAYOUT = 0x0800,
- CONFIG_UI_MODE = 0x1000
+ CONFIG_MCC = ACONFIGURATION_MCC,
+ CONFIG_MNC = ACONFIGURATION_MCC,
+ CONFIG_LOCALE = ACONFIGURATION_LOCALE,
+ CONFIG_TOUCHSCREEN = ACONFIGURATION_TOUCHSCREEN,
+ CONFIG_KEYBOARD = ACONFIGURATION_KEYBOARD,
+ CONFIG_KEYBOARD_HIDDEN = ACONFIGURATION_KEYBOARD_HIDDEN,
+ CONFIG_NAVIGATION = ACONFIGURATION_NAVIGATION,
+ CONFIG_ORIENTATION = ACONFIGURATION_ORIENTATION,
+ CONFIG_DENSITY = ACONFIGURATION_DENSITY,
+ CONFIG_SCREEN_SIZE = ACONFIGURATION_SCREEN_SIZE,
+ CONFIG_VERSION = ACONFIGURATION_VERSION,
+ CONFIG_SCREEN_LAYOUT = ACONFIGURATION_SCREEN_LAYOUT,
+ CONFIG_UI_MODE = ACONFIGURATION_UI_MODE
};
// Compare two configuration, returning CONFIG_* flags set for each value
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 8d00e85..e807aba 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -69,16 +69,16 @@
int width = (int) glyph->mBitmapWidth;
int height = (int) glyph->mBitmapHeight;
- if(bounds->bottom > nPenY) {
+ if (bounds->bottom > nPenY) {
bounds->bottom = nPenY;
}
- if(bounds->left > nPenX) {
+ if (bounds->left > nPenX) {
bounds->left = nPenX;
}
- if(bounds->right < nPenX + width) {
+ if (bounds->right < nPenX + width) {
bounds->right = nPenX + width;
}
- if(bounds->top < nPenY + height) {
+ if (bounds->top < nPenY + height) {
bounds->top = nPenY + height;
}
}
@@ -102,7 +102,7 @@
}
void Font::drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
- uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
+ uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
int nPenX = x + glyph->mBitmapLeft;
int nPenY = y + glyph->mBitmapTop;
@@ -116,7 +116,7 @@
int32_t bX = 0, bY = 0;
for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
- if(bX < 0 || bY < 0 || bX >= (int32_t)bitmapW || bY >= (int32_t)bitmapH) {
+ if (bX < 0 || bY < 0 || bX >= (int32_t)bitmapW || bY >= (int32_t)bitmapH) {
LOGE("Skipping invalid index");
continue;
}
@@ -143,22 +143,19 @@
}
void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y,
- uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
- if(bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
- renderUTF(paint, text, start, len, numGlyphs, x, y, BITMAP,
- bitmap, bitmapW, bitmapH, NULL);
- }
- else {
- renderUTF(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER,
- NULL, 0, 0, NULL);
+ int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
+ if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
+ renderUTF(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
+ bitmapW, bitmapH, NULL);
+ } else {
+ renderUTF(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 0, 0, NULL);
}
}
void Font::measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, Rect *bounds) {
- if(bounds == NULL) {
+ int numGlyphs, Rect *bounds) {
+ if (bounds == NULL) {
LOGE("No return rectangle provided to measure text");
return;
}
@@ -167,9 +164,8 @@
}
void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, RenderMode mode,
- uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
- Rect *bounds) {
+ int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
+ uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
if (numGlyphs == 0 || text == NULL || len == 0) {
return;
}
@@ -185,7 +181,7 @@
while (glyphsLeft > 0) {
int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text);
- // Reached the end of the string or encountered
+ // Reached the end of the string
if (utfChar < 0) {
break;
}
@@ -422,7 +418,7 @@
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// Initialize texture dimentions
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
- GL_ALPHA, GL_UNSIGNED_BYTE, 0);
+ GL_ALPHA, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -527,7 +523,6 @@
}
void FontRenderer::issueDrawCommand() {
-
checkTextureUpdate();
float* vtx = mTextMeshPtr;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 5d30b1a..aaf7564 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -435,6 +435,7 @@
return;
}
+ glActiveTexture(GL_TEXTURE0);
const Texture* texture = mTextureCache.get(bitmap);
if (!texture) return;
const AutoTexture autoCleanup(texture);
@@ -451,6 +452,7 @@
return;
}
+ glActiveTexture(GL_TEXTURE0);
const Texture* texture = mTextureCache.get(bitmap);
if (!texture) return;
const AutoTexture autoCleanup(texture);
@@ -466,6 +468,7 @@
return;
}
+ glActiveTexture(GL_TEXTURE0);
const Texture* texture = mTextureCache.get(bitmap);
if (!texture) return;
const AutoTexture autoCleanup(texture);
@@ -491,6 +494,7 @@
return;
}
+ glActiveTexture(GL_TEXTURE0);
const Texture* texture = mTextureCache.get(bitmap);
if (!texture) return;
const AutoTexture autoCleanup(texture);
@@ -538,13 +542,17 @@
drawColorRect(left, top, right, bottom, color, mode);
}
+#define kStdStrikeThru_Offset (-6.0f / 21.0f)
+#define kStdUnderline_Offset (1.0f / 9.0f)
+#define kStdUnderline_Thickness (1.0f / 18.0f)
+
void OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
float x, float y, SkPaint* paint) {
if (text == NULL || count == 0 || (paint->getAlpha() == 0 && paint->getXfermode() == NULL)) {
return;
}
- float length;
+ float length = -1.0f;
switch (paint->getTextAlign()) {
case SkPaint::kCenter_Align:
length = paint->measureText(text, bytesCount);
@@ -607,13 +615,56 @@
mColorFilter->setupProgram(mCurrentProgram);
}
- // TODO: Implement scale properly
const Rect& clip = mSnapshot->getLocalClip();
mFontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()), paint->getTextSize());
mFontRenderer.renderText(paint, &clip, text, 0, bytesCount, count, x, y);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(texCoordsSlot);
+
+ // Handle underline and strike-through
+ uint32_t flags = paint->getFlags();
+ if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
+ float underlineWidth = length;
+ // If length is > 0.0f, we already measured the text for the text alignment
+ if (length <= 0.0f) {
+ underlineWidth = paint->measureText(text, bytesCount);
+ }
+
+ float offsetX = 0;
+ switch (paint->getTextAlign()) {
+ case SkPaint::kCenter_Align:
+ offsetX = underlineWidth * 0.5f;
+ break;
+ case SkPaint::kRight_Align:
+ offsetX = underlineWidth;
+ break;
+ default:
+ break;
+ }
+
+ if (underlineWidth > 0.0f) {
+ float textSize = paint->getTextSize();
+ float height = textSize * kStdUnderline_Thickness;
+
+ float left = x - offsetX;
+ float top = 0.0f;
+ float right = left + underlineWidth;
+ float bottom = 0.0f;
+
+ if (flags & SkPaint::kUnderlineText_Flag) {
+ top = y + textSize * kStdUnderline_Offset;
+ bottom = top + height;
+ drawRect(left, top, right, bottom, paint);
+ }
+
+ if (flags & SkPaint::kStrikeThruText_Flag) {
+ top = y + textSize * kStdStrikeThru_Offset;
+ bottom = top + height;
+ drawRect(left, top, right, bottom, paint);
+ }
+ }
+ }
}
void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) {
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 4a01ffa..9a22dc0 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -162,7 +162,8 @@
glGenTextures(1, &texture->id);
glBindTexture(GL_TEXTURE_2D, texture->id);
- glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap.bytesPerPixel());
+ // Textures are Alpha8
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
texture->blend = true;
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, bitmap.rowBytesAsPixels(), texture->height, 0,
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 8a97b4c..39fe85a 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -137,7 +137,7 @@
// PorterDuff snippets
///////////////////////////////////////////////////////////////////////////////
-const char* gPorterDuff[12] = {
+const char* gBlendOps[18] = {
// Clear
"return vec4(0.0, 0.0, 0.0, 0.0);\n",
// Src
@@ -161,8 +161,26 @@
// DstAtop
"return vec4(dst.rgb * src.a + (1.0 - dst.a) * src.rgb, src.a);\n",
// Xor
- "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb, 1.0, "
+ "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb, "
"src.a + dst.a - 2.0 * src.a * dst.a);\n",
+ // Add
+ "return min(src + dst, 1.0);\n",
+ // Multiply
+ "return src * dst;\n",
+ // Screen
+ "return src + dst - src * dst;\n",
+ // Overlay
+ "return clamp(vec4(mix("
+ "2.0 * src.rgb * dst.rgb + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a), "
+ "src.a * dst.a - 2.0 * (dst.a - dst.rgb) * (src.a - src.rgb) + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a), "
+ "step(dst.a, 2.0 * dst.rgb)), "
+ "src.a + dst.a - src.a * dst.a), 0.0, 1.0);\n",
+ // Darken
+ "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb + "
+ "min(src.rgb * dst.a, dst.rgb * src.a), src.a + dst.a - src.a * dst.a);\n",
+ // Lighten
+ "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb + "
+ "max(src.rgb * dst.a, dst.rgb * src.a), src.a + dst.a - src.a * dst.a);\n",
};
///////////////////////////////////////////////////////////////////////////////
@@ -292,10 +310,10 @@
// Generate required functions
if (description.hasGradient && description.hasBitmap) {
- generatePorterDuffBlend(shader, "blendShaders", description.shadersMode);
+ generateBlend(shader, "blendShaders", description.shadersMode);
}
if (description.colorOp == ProgramDescription::kColorBlend) {
- generatePorterDuffBlend(shader, "blendColors", description.colorMode);
+ generateBlend(shader, "blendColors", description.colorMode);
}
if (description.isBitmapNpot) {
generateTextureWrap(shader, description.bitmapWrapS, description.bitmapWrapT);
@@ -354,13 +372,12 @@
return shader;
}
-void ProgramCache::generatePorterDuffBlend(String8& shader, const char* name,
- SkXfermode::Mode mode) {
+void ProgramCache::generateBlend(String8& shader, const char* name, SkXfermode::Mode mode) {
shader.append("\nvec4 ");
shader.append(name);
shader.append("(vec4 src, vec4 dst) {\n");
shader.append(" ");
- shader.append(gPorterDuff[mode]);
+ shader.append(gBlendOps[mode]);
shader.append("}\n");
}
@@ -376,6 +393,9 @@
}
shader.append(" return vec2(");
switch (wrapS) {
+ case GL_CLAMP_TO_EDGE:
+ shader.append("texCoords.x");
+ break;
case GL_REPEAT:
shader.append("mod(texCoords.x, 1.0)");
break;
@@ -385,6 +405,9 @@
}
shader.append(", ");
switch (wrapT) {
+ case GL_CLAMP_TO_EDGE:
+ shader.append("texCoords.y");
+ break;
case GL_REPEAT:
shader.append("mod(texCoords.y, 1.0)");
break;
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index a1a4a0e..54850ee 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -35,7 +35,7 @@
///////////////////////////////////////////////////////////////////////////////
// Debug
-#define DEBUG_PROGRAM_CACHE 0
+#define DEBUG_PROGRAM_CACHE 1
// Debug
#if DEBUG_PROGRAM_CACHE
@@ -57,9 +57,9 @@
#define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600
#define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800
-// Support only the 12 Porter-Duff modes for now
-#define PROGRAM_MAX_XFERMODE 0xC
-#define PROGRAM_XFERMODE_SHADER_SHIFT 24
+// Encode the xfermodes on 6 bits
+#define PROGRAM_MAX_XFERMODE 0x1f
+#define PROGRAM_XFERMODE_SHADER_SHIFT 26
#define PROGRAM_XFERMODE_COLOR_OP_SHIFT 20
#define PROGRAM_BITMAP_WRAPS_SHIFT 9
@@ -177,7 +177,7 @@
Program* generateProgram(const ProgramDescription& description, programid key);
String8 generateVertexShader(const ProgramDescription& description);
String8 generateFragmentShader(const ProgramDescription& description);
- void generatePorterDuffBlend(String8& shader, const char* name, SkXfermode::Mode mode);
+ void generateBlend(String8& shader, const char* name, SkXfermode::Mode mode);
void generateTextureWrap(String8& shader, GLenum wrapS, GLenum wrapT);
void printLongString(const String8& shader) const;
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 42c0621..3569d6a 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -89,7 +89,8 @@
description.hasBitmap = true;
// The driver does not support non-power of two mirrored/repeated
// textures, so do it ourselves
- if (!extensions.hasNPot() && !isPowerOfTwo(width) && !isPowerOfTwo(height)) {
+ if (!extensions.hasNPot() && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) &&
+ (mTileX != SkShader::kClamp_TileMode || mTileY != SkShader::kClamp_TileMode)) {
description.isBitmapNpot = true;
description.bitmapWrapS = gTileModes[mTileX];
description.bitmapWrapT = gTileModes[mTileY];
diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/vertical_blur.rs b/libs/rs/java/ImageProcessing/src/com/android/rs/image/vertical_blur.rs
index 008c8b5..3c69289 100644
--- a/libs/rs/java/ImageProcessing/src/com/android/rs/image/vertical_blur.rs
+++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/vertical_blur.rs
@@ -8,52 +8,22 @@
const uchar4 *input = (const uchar4 *)rsGetElementAt(fs->ain, x, 0);
float3 blurredPixel = 0;
- float3 currentPixel = 0;
-
const float *gPtr = fs->gaussian;
if ((y > fs->radius) && (y < (fs->height - fs->radius))) {
const uchar4 *i = input + ((y - fs->radius) * fs->width);
for(int r = -fs->radius; r <= fs->radius; r ++) {
- currentPixel.x = (float)(i->x);
- currentPixel.y = (float)(i->y);
- currentPixel.z = (float)(i->z);
- blurredPixel += currentPixel * gPtr[0];
+ blurredPixel += convert_float3(i->xyz) * gPtr[0];
gPtr++;
i += fs->width;
}
} else {
for(int r = -fs->radius; r <= fs->radius; r ++) {
- #if 1
- int validH = y + r;
- // Clamp to zero and width
- if(validH < 0) {
- validH = 0;
- }
- if(validH > fs->height - 1) {
- validH = fs->height - 1;
- }
-
+ int validH = rsClamp(y + r, (uint)0, (uint)(fs->height - 1));
const uchar4 *i = input + validH * fs->width;
- //const uchar4 *i = (const uchar4 *)rsGetElementAt(fs->ain, x, validH);
-
- currentPixel.x = (float)(i->x);
- currentPixel.y = (float)(i->y);
- currentPixel.z = (float)(i->z);
- blurredPixel += currentPixel * gPtr[0];
+ blurredPixel += convert_float3(i->xyz) * gPtr[0];
gPtr++;
- #else
- int validH = rsClamp(y + r, 0, height - 1);
- validH -= y;
- uchar4 *i = input + validH * width + x;
- blurredPixel.xyz += convert_float3(i->xyz) * gPtr[0];
- gPtr++;
- #endif
}
}
-
- //output->xyz = convert_uchar3(blurredPixel.xyz);
- output->x = (uint8_t)blurredPixel.x;
- output->y = (uint8_t)blurredPixel.y;
- output->z = (uint8_t)blurredPixel.z;
+ output->xyz = convert_uchar3(blurredPixel);
}
diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp
index 6560101..d62fa55 100644
--- a/libs/rs/rsAllocation.cpp
+++ b/libs/rs/rsAllocation.cpp
@@ -234,6 +234,12 @@
LOGE("Allocation::data called with mismatched size expected %i, got %i", size, sizeBytes);
return;
}
+
+ if (mType->getElement()->getHasReferences()) {
+ incRefs(data, sizeBytes / mType->getElement()->getSizeBytes());
+ decRefs(mPtr, sizeBytes / mType->getElement()->getSizeBytes());
+ }
+
memcpy(mPtr, data, size);
sendDirty();
mUploadDefered = true;
@@ -256,6 +262,12 @@
mType->dumpLOGV("type info");
return;
}
+
+ if (mType->getElement()->getHasReferences()) {
+ incRefs(data, count);
+ decRefs(ptr, count);
+ }
+
memcpy(ptr, data, size);
sendDirty();
mUploadDefered = true;
@@ -279,6 +291,10 @@
for (uint32_t line=yoff; line < (yoff+h); line++) {
uint8_t * ptr = static_cast<uint8_t *>(mPtr);
+ if (mType->getElement()->getHasReferences()) {
+ incRefs(src, w);
+ decRefs(dst, w);
+ }
memcpy(dst, src, lineSize);
src += lineSize;
dst += destW * eSize;
@@ -387,6 +403,32 @@
}
}
+void Allocation::incRefs(const void *ptr, size_t ct) const
+{
+ const uint8_t *p = static_cast<const uint8_t *>(ptr);
+ const Element *e = mType->getElement();
+ uint32_t stride = e->getSizeBytes();
+
+ while (ct > 0) {
+ e->incRefs(p);
+ ct --;
+ p += stride;
+ }
+}
+
+void Allocation::decRefs(const void *ptr, size_t ct) const
+{
+ const uint8_t *p = static_cast<const uint8_t *>(ptr);
+ const Element *e = mType->getElement();
+ uint32_t stride = e->getSizeBytes();
+
+ while (ct > 0) {
+ e->decRefs(p);
+ ct --;
+ p += stride;
+ }
+}
+
/////////////////
//
diff --git a/libs/rs/rsAllocation.h b/libs/rs/rsAllocation.h
index 8273165..177d5a4 100644
--- a/libs/rs/rsAllocation.h
+++ b/libs/rs/rsAllocation.h
@@ -81,6 +81,9 @@
bool getIsTexture() const {return mIsTexture;}
bool getIsBufferObject() const {return mIsVertexBuffer;}
+ void incRefs(const void *ptr, size_t ct) const;
+ void decRefs(const void *ptr, size_t ct) const;
+
protected:
void sendDirty() const;
diff --git a/libs/rs/rsComponent.cpp b/libs/rs/rsComponent.cpp
index 8e509ad..fbaa75f 100644
--- a/libs/rs/rsComponent.cpp
+++ b/libs/rs/rsComponent.cpp
@@ -161,6 +161,10 @@
mBits = mTypeBits * mVectorSize;
}
+bool Component::isReference() const
+{
+ return (mType >= RS_TYPE_ELEMENT);
+}
diff --git a/libs/rs/rsComponent.h b/libs/rs/rsComponent.h
index 15fd5dd..a775051 100644
--- a/libs/rs/rsComponent.h
+++ b/libs/rs/rsComponent.h
@@ -51,6 +51,8 @@
void serialize(OStream *stream) const;
void loadFromStream(IStream *stream);
+ bool isReference() const;
+
protected:
RsDataType mType;
RsDataKind mKind;
diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp
index 37b8bd6..05902f9 100644
--- a/libs/rs/rsElement.cpp
+++ b/libs/rs/rsElement.cpp
@@ -34,6 +34,7 @@
mAllocLine = __LINE__;
mFields = NULL;
mFieldCount = 0;
+ mHasReference = false;
}
@@ -53,6 +54,7 @@
delete [] mFields;
mFields = NULL;
mFieldCount = 0;
+ mHasReference = false;
}
size_t Element::getSizeBits() const
@@ -68,15 +70,6 @@
return total;
}
-size_t Element::getFieldOffsetBits(uint32_t componentNumber) const
-{
- size_t offset = 0;
- for (uint32_t ct = 0; ct < componentNumber; ct++) {
- offset += mFields[ct].e->mBits;
- }
- return offset;
-}
-
void Element::dumpLOGV(const char *prefix) const
{
ObjectBase::dumpLOGV(prefix);
@@ -124,11 +117,14 @@
elem->mFieldCount = stream->loadU32();
if(elem->mFieldCount) {
+ uint32_t offset = 0;
elem->mFields = new ElementField_t [elem->mFieldCount];
for(uint32_t ct = 0; ct < elem->mFieldCount; ct ++) {
stream->loadString(&elem->mFields[ct].name);
Element *fieldElem = Element::createFromStream(rsc, stream);
elem->mFields[ct].e.set(fieldElem);
+ elem->mFields[ct].offsetBits = offset;
+ offset += fieldElem->getSizeBits();
}
}
@@ -193,6 +189,7 @@
Element *e = new Element(rsc);
e->mComponent.set(dt, dk, isNorm, vecSize);
e->mBits = e->mComponent.getBits();
+ e->mHasReference = e->mComponent.isReference();
rsc->mStateElement.mElements.push(e);
return e;
}
@@ -223,9 +220,16 @@
Element *e = new Element(rsc);
e->mFields = new ElementField_t [count];
e->mFieldCount = count;
+ size_t bits = 0;
for (size_t ct=0; ct < count; ct++) {
e->mFields[ct].e.set(ein[ct]);
e->mFields[ct].name.setTo(nin[ct], lengths[ct]);
+ e->mFields[ct].offsetBits = bits;
+ bits += ein[ct]->getSizeBits();
+
+ if (ein[ct]->mHasReference) {
+ e->mHasReference = true;
+ }
}
rsc->mStateElement.mElements.push(e);
@@ -251,6 +255,43 @@
return s;
}
+void Element::incRefs(const void *ptr) const
+{
+ if (!mFieldCount) {
+ if (mComponent.isReference()) {
+ ObjectBase *const*obp = static_cast<ObjectBase *const*>(ptr);
+ ObjectBase *ob = obp[0];
+ ob->incSysRef();
+ }
+ return;
+ }
+
+ const uint8_t *p = static_cast<const uint8_t *>(ptr);
+ for (uint32_t i=0; i < mFieldCount; i++) {
+ if (mFields[i].e->mHasReference) {
+ mFields[i].e->incRefs(&p[mFields[i].offsetBits >> 3]);
+ }
+ }
+}
+
+void Element::decRefs(const void *ptr) const
+{
+ if (!mFieldCount) {
+ if (mComponent.isReference()) {
+ ObjectBase *const*obp = static_cast<ObjectBase *const*>(ptr);
+ ObjectBase *ob = obp[0];
+ ob->decSysRef();
+ }
+ return;
+ }
+
+ const uint8_t *p = static_cast<const uint8_t *>(ptr);
+ for (uint32_t i=0; i < mFieldCount; i++) {
+ if (mFields[i].e->mHasReference) {
+ mFields[i].e->decRefs(&p[mFields[i].offsetBits >> 3]);
+ }
+ }
+}
ElementState::ElementState()
diff --git a/libs/rs/rsElement.h b/libs/rs/rsElement.h
index 90e7cc8..b5dad7a2 100644
--- a/libs/rs/rsElement.h
+++ b/libs/rs/rsElement.h
@@ -40,9 +40,11 @@
return (getSizeBits() + 7) >> 3;
}
- size_t getFieldOffsetBits(uint32_t componentNumber) const;
+ size_t getFieldOffsetBits(uint32_t componentNumber) const {
+ return mFields[componentNumber].offsetBits;
+ }
size_t getFieldOffsetBytes(uint32_t componentNumber) const {
- return (getFieldOffsetBits(componentNumber) + 7) >> 3;
+ return mFields[componentNumber].offsetBits >> 3;
}
uint32_t getFieldCount() const {return mFieldCount;}
@@ -66,6 +68,10 @@
static const Element * create(Context *rsc, size_t count, const Element **,
const char **, const size_t * lengths);
+ void incRefs(const void *) const;
+ void decRefs(const void *) const;
+ bool getHasReferences() const {return mHasReference;}
+
protected:
// deallocate any components that are part of this element.
void clear();
@@ -73,9 +79,11 @@
typedef struct {
String8 name;
ObjectBaseRef<const Element> e;
+ uint32_t offsetBits;
} ElementField_t;
ElementField_t *mFields;
size_t mFieldCount;
+ bool mHasReference;
Element(Context *);
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 6618702..5f5a4ac 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -1080,6 +1080,14 @@
1, & pointerId, pointerCoords, mXPrecision, mYPrecision, downTime);
}
+int32_t TrackballInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+ if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) {
+ return getEventHub()->getScanCodeState(getDeviceId(), scanCode);
+ } else {
+ return AKEY_STATE_UNKNOWN;
+ }
+}
+
// --- TouchInputMapper ---
diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp
index 60a0d82..e09e755 100644
--- a/libs/utils/AssetManager.cpp
+++ b/libs/utils/AssetManager.cpp
@@ -232,6 +232,12 @@
}
}
+void AssetManager::getConfiguration(ResTable_config* outConfig) const
+{
+ AutoMutex _l(mLock);
+ *outConfig = *mConfig;
+}
+
/*
* Open an asset.
*
diff --git a/media/java/android/media/AudioEffect.java b/media/java/android/media/AudioEffect.java
index aed29c3..35038fa 100644
--- a/media/java/android/media/AudioEffect.java
+++ b/media/java/android/media/AudioEffect.java
@@ -101,15 +101,15 @@
public static final int STATE_INITIALIZED = 1;
// to keep in sync with
- // frameworks/base/media/jni/audioeffect/android_media_AudioEffect.cpp
- /**
- * Event id for engine state change notification.
- */
- public static final int NATIVE_EVENT_ENABLED_STATUS = 0;
+ // frameworks/base/include/media/AudioEffect.h
/**
* Event id for engine control ownership change notification.
*/
- public static final int NATIVE_EVENT_CONTROL_STATUS = 1;
+ public static final int NATIVE_EVENT_CONTROL_STATUS = 0;
+ /**
+ * Event id for engine state change notification.
+ */
+ public static final int NATIVE_EVENT_ENABLED_STATUS = 1;
/**
* Event id for engine parameter change notification.
*/
@@ -795,7 +795,7 @@
// Interface definitions
// --------------------
/**
- * The OnParameterChangeListener interface defines a method called by the AudioEffect
+ * The OnEnableStatusChangeListener interface defines a method called by the AudioEffect
* when a the enabled state of the effect engine was changed by the controlling application.
*/
public interface OnEnableStatusChangeListener {
@@ -922,7 +922,6 @@
if (effect == null) {
return;
}
-
if (effect.mNativeEventHandler != null) {
Message m = effect.mNativeEventHandler.obtainMessage(what, arg1,
arg2, obj);
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 9212708..41d2cc5 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -239,6 +239,9 @@
// independently change its priority)
private final BroadcastReceiver mMediaButtonReceiver = new MediaButtonBroadcastReceiver();
+ // Used to alter media button redirection when the phone is ringing.
+ private boolean mIsRinging = false;
+
// Devices currently connected
private HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
@@ -1956,11 +1959,16 @@
private final static Object mAudioFocusLock = new Object();
+ private final static Object mRingingLock = new Object();
+
private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
if (state == TelephonyManager.CALL_STATE_RINGING) {
//Log.v(TAG, " CALL_STATE_RINGING");
+ synchronized(mRingingLock) {
+ mIsRinging = true;
+ }
int ringVolume = AudioService.this.getStreamVolume(AudioManager.STREAM_RING);
if (ringVolume > 0) {
requestAudioFocus(AudioManager.STREAM_RING,
@@ -1970,12 +1978,18 @@
}
} else if (state == TelephonyManager.CALL_STATE_OFFHOOK) {
//Log.v(TAG, " CALL_STATE_OFFHOOK");
+ synchronized(mRingingLock) {
+ mIsRinging = false;
+ }
requestAudioFocus(AudioManager.STREAM_RING,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,
null, null /* both allowed to be null only for this clientId */,
IN_VOICE_COMM_FOCUS_ID /*clientId*/);
} else if (state == TelephonyManager.CALL_STATE_IDLE) {
//Log.v(TAG, " CALL_STATE_IDLE");
+ synchronized(mRingingLock) {
+ mIsRinging = false;
+ }
abandonAudioFocus(null, IN_VOICE_COMM_FOCUS_ID);
}
}
@@ -2243,9 +2257,11 @@
// if in a call or ringing, do not break the current phone app behavior
// TODO modify this to let the phone app specifically get the RC focus
// add modify the phone app to take advantage of the new API
- if ((getMode() == AudioSystem.MODE_IN_CALL) ||
- (getMode() == AudioSystem.MODE_RINGTONE)) {
- return;
+ synchronized(mRingingLock) {
+ if (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL) ||
+ (getMode() == AudioSystem.MODE_RINGTONE) ) {
+ return;
+ }
}
synchronized(mRCStack) {
if (!mRCStack.empty()) {
diff --git a/media/java/android/media/MtpDatabase.java b/media/java/android/media/MtpDatabase.java
index 37f9f2c..88cce46 100644
--- a/media/java/android/media/MtpDatabase.java
+++ b/media/java/android/media/MtpDatabase.java
@@ -220,6 +220,50 @@
return -1;
}
+ private int[] getSupportedPlaybackFormats() {
+ return new int[] {
+ Mtp.Object.FORMAT_ASSOCIATION,
+ Mtp.Object.FORMAT_MP3,
+ Mtp.Object.FORMAT_MPEG,
+ Mtp.Object.FORMAT_EXIF_JPEG,
+ Mtp.Object.FORMAT_TIFF_EP,
+ Mtp.Object.FORMAT_GIF,
+ Mtp.Object.FORMAT_JFIF,
+ Mtp.Object.FORMAT_PNG,
+ Mtp.Object.FORMAT_TIFF,
+ Mtp.Object.FORMAT_WMA,
+ Mtp.Object.FORMAT_OGG,
+ Mtp.Object.FORMAT_AAC,
+ Mtp.Object.FORMAT_MP4_CONTAINER,
+ Mtp.Object.FORMAT_MP2,
+ Mtp.Object.FORMAT_3GP_CONTAINER,
+ Mtp.Object.FORMAT_ABSTRACT_AV_PLAYLIST,
+ Mtp.Object.FORMAT_WPL_PLAYLIST,
+ Mtp.Object.FORMAT_M3U_PLAYLIST,
+ Mtp.Object.FORMAT_PLS_PLAYLIST,
+ };
+ }
+
+ private int[] getSupportedCaptureFormats() {
+ // no capture formats yet
+ return null;
+ }
+
+ private int[] getSupportedObjectProperties(int handle) {
+ return new int[] {
+ Mtp.Object.PROPERTY_STORAGE_ID,
+ Mtp.Object.PROPERTY_OBJECT_FORMAT,
+ Mtp.Object.PROPERTY_OBJECT_SIZE,
+ Mtp.Object.PROPERTY_OBJECT_FILE_NAME,
+ Mtp.Object.PROPERTY_PARENT_OBJECT,
+ };
+ }
+
+ private int[] getSupportedDeviceProperties() {
+ // no device properties yet
+ return null;
+ }
+
private int getObjectProperty(int handle, int property,
long[] outIntValue, char[] outStringValue) {
Log.d(TAG, "getObjectProperty: " + property);
diff --git a/media/jni/android_media_MtpDatabase.cpp b/media/jni/android_media_MtpDatabase.cpp
index bfdc872..abbea30 100644
--- a/media/jni/android_media_MtpDatabase.cpp
+++ b/media/jni/android_media_MtpDatabase.cpp
@@ -40,6 +40,10 @@
static jmethodID method_endSendObject;
static jmethodID method_getObjectList;
static jmethodID method_getNumObjects;
+static jmethodID method_getSupportedPlaybackFormats;
+static jmethodID method_getSupportedCaptureFormats;
+static jmethodID method_getSupportedObjectProperties;
+static jmethodID method_getSupportedDeviceProperties;
static jmethodID method_getObjectProperty;
static jmethodID method_getObjectInfo;
static jmethodID method_getObjectFilePath;
@@ -87,6 +91,13 @@
MtpObjectFormat format,
MtpObjectHandle parent);
+ // callee should delete[] the results from these
+ // results can be NULL
+ virtual MtpObjectFormatList* getSupportedPlaybackFormats();
+ virtual MtpObjectFormatList* getSupportedCaptureFormats();
+ virtual MtpObjectPropertyList* getSupportedObjectProperties(MtpObjectFormat format);
+ virtual MtpDevicePropertyList* getSupportedDeviceProperties();
+
virtual MtpResponseCode getObjectProperty(MtpObjectHandle handle,
MtpObjectProperty property,
MtpDataPacket& packet);
@@ -190,6 +201,66 @@
(jint)storageID, (jint)format, (jint)parent);
}
+MtpObjectFormatList* MyMtpDatabase::getSupportedPlaybackFormats() {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
+ method_getSupportedPlaybackFormats);
+ if (!array)
+ return NULL;
+ MtpObjectFormatList* list = new MtpObjectFormatList();
+ jint* formats = env->GetIntArrayElements(array, 0);
+ jsize length = env->GetArrayLength(array);
+ for (int i = 0; i < length; i++)
+ list->push(formats[i]);
+ env->ReleaseIntArrayElements(array, formats, 0);
+ return list;
+}
+
+MtpObjectFormatList* MyMtpDatabase::getSupportedCaptureFormats() {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
+ method_getSupportedCaptureFormats);
+ if (!array)
+ return NULL;
+ MtpObjectFormatList* list = new MtpObjectFormatList();
+ jint* formats = env->GetIntArrayElements(array, 0);
+ jsize length = env->GetArrayLength(array);
+ for (int i = 0; i < length; i++)
+ list->push(formats[i]);
+ env->ReleaseIntArrayElements(array, formats, 0);
+ return list;
+}
+
+MtpObjectPropertyList* MyMtpDatabase::getSupportedObjectProperties(MtpObjectFormat format) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
+ method_getSupportedObjectProperties, (jint)format);
+ if (!array)
+ return NULL;
+ MtpObjectPropertyList* list = new MtpObjectPropertyList();
+ jint* properties = env->GetIntArrayElements(array, 0);
+ jsize length = env->GetArrayLength(array);
+ for (int i = 0; i < length; i++)
+ list->push(properties[i]);
+ env->ReleaseIntArrayElements(array, properties, 0);
+ return list;
+}
+
+MtpDevicePropertyList* MyMtpDatabase::getSupportedDeviceProperties() {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
+ method_getSupportedDeviceProperties);
+ if (!array)
+ return NULL;
+ MtpDevicePropertyList* list = new MtpDevicePropertyList();
+ jint* properties = env->GetIntArrayElements(array, 0);
+ jsize length = env->GetArrayLength(array);
+ for (int i = 0; i < length; i++)
+ list->push(properties[i]);
+ env->ReleaseIntArrayElements(array, properties, 0);
+ return list;
+}
+
MtpResponseCode MyMtpDatabase::getObjectProperty(MtpObjectHandle handle,
MtpObjectProperty property,
MtpDataPacket& packet) {
@@ -460,6 +531,26 @@
LOGE("Can't find getNumObjects");
return -1;
}
+ method_getSupportedPlaybackFormats = env->GetMethodID(clazz, "getSupportedPlaybackFormats", "()[I");
+ if (method_getSupportedPlaybackFormats == NULL) {
+ LOGE("Can't find getSupportedPlaybackFormats");
+ return -1;
+ }
+ method_getSupportedCaptureFormats = env->GetMethodID(clazz, "getSupportedCaptureFormats", "()[I");
+ if (method_getSupportedCaptureFormats == NULL) {
+ LOGE("Can't find getSupportedCaptureFormats");
+ return -1;
+ }
+ method_getSupportedObjectProperties = env->GetMethodID(clazz, "getSupportedObjectProperties", "(I)[I");
+ if (method_getSupportedObjectProperties == NULL) {
+ LOGE("Can't find getSupportedObjectProperties");
+ return -1;
+ }
+ method_getSupportedDeviceProperties = env->GetMethodID(clazz, "getSupportedDeviceProperties", "()[I");
+ if (method_getSupportedDeviceProperties == NULL) {
+ LOGE("Can't find getSupportedDeviceProperties");
+ return -1;
+ }
method_getObjectProperty = env->GetMethodID(clazz, "getObjectProperty", "(II[J[C)I");
if (method_getObjectProperty == NULL) {
LOGE("Can't find getObjectProperty");
diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp
index 3cdf48a..0f3e245 100644
--- a/media/libmedia/AudioEffect.cpp
+++ b/media/libmedia/AudioEffect.cpp
@@ -218,7 +218,7 @@
return mIEffect->disable();
}
}
- return INVALID_OPERATION;
+ return NO_ERROR;
}
status_t AudioEffect::command(uint32_t cmdCode,
@@ -231,7 +231,22 @@
return INVALID_OPERATION;
}
- return mIEffect->command(cmdCode, cmdSize, cmdData, replySize, replyData);
+ status_t status = mIEffect->command(cmdCode, cmdSize, cmdData, replySize, replyData);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ status = *(status_t *)replyData;
+ if (status != NO_ERROR) {
+ return status;
+ }
+
+ if (cmdCode == EFFECT_CMD_ENABLE) {
+ android_atomic_or(1, &mEnabled);
+ }
+ if (cmdCode == EFFECT_CMD_DISABLE) {
+ android_atomic_and(~1, &mEnabled);
+ }
+ return status;
}
@@ -347,7 +362,11 @@
{
LOGV("enableStatusChanged %p enabled %d mCbf %p", this, enabled, mCbf);
if (mStatus == ALREADY_EXISTS) {
- mEnabled = enabled;
+ if (enabled) {
+ android_atomic_or(1, &mEnabled);
+ } else {
+ android_atomic_and(~1, &mEnabled);
+ }
if (mCbf) {
mCbf(EVENT_ENABLE_STATUS_CHANGED, mUserData, &enabled);
}
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 77a1476..404762f 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -81,6 +81,7 @@
libstagefright_httplive \
libstagefright_rtsp \
libstagefright_id3 \
+ libstagefright_g711dec \
LOCAL_SHARED_LIBRARIES += \
libstagefright_amrnb_common \
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index e426fca..efdad43 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -854,18 +854,6 @@
status_t AwesomePlayer::initVideoDecoder() {
uint32_t flags = 0;
-#if 0
- if (mRTPSession != NULL) {
- // XXX hack.
-
- const char *mime;
- CHECK(mVideoTrack->getFormat()->findCString(kKeyMIMEType, &mime));
- if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
- flags |= OMXCodec::kPreferSoftwareCodecs;
- }
- }
-#endif
-
mVideoSource = OMXCodec::Create(
mClient.interface(), mVideoTrack->getFormat(),
false, // createEncoder
@@ -1019,6 +1007,12 @@
int64_t latenessUs = nowUs - timeUs;
+ if (mRTPSession != NULL) {
+ // We'll completely ignore timestamps for gtalk videochat
+ // and we'll play incoming video as fast as we get it.
+ latenessUs = 0;
+ }
+
if (latenessUs > 40000) {
// We're more than 40ms late.
LOGV("we're late by %lld us (%.2f secs)", latenessUs, latenessUs / 1E6);
diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp
index 23d8f56..1fd256a 100644
--- a/media/libstagefright/CameraSourceTimeLapse.cpp
+++ b/media/libstagefright/CameraSourceTimeLapse.cpp
@@ -149,10 +149,10 @@
LOGV("threadTimeLapseEntry: taking picture");
CHECK_EQ(OK, mCamera->takePicture());
mCameraIdle = false;
- sleep(mTimeBetweenTimeLapseFrameCaptureUs/1E6);
+ usleep(mTimeBetweenTimeLapseFrameCaptureUs);
} else {
LOGV("threadTimeLapseEntry: camera busy with old takePicture. Sleeping a little.");
- sleep(.01);
+ usleep(1E4);
}
}
}
diff --git a/media/libstagefright/HTTPStream.cpp b/media/libstagefright/HTTPStream.cpp
index 9c99866..ccc6a34 100644
--- a/media/libstagefright/HTTPStream.cpp
+++ b/media/libstagefright/HTTPStream.cpp
@@ -68,7 +68,7 @@
return UNKNOWN_ERROR;
}
- setReceiveTimeout(5); // Time out reads after 5 secs by default
+ setReceiveTimeout(30); // Time out reads after 30 secs by default
mState = CONNECTING;
@@ -158,7 +158,7 @@
// The workaround accepts both behaviours but could potentially break
// legitimate responses that use a single newline to "fold" headers, which is
// why it's not yet on by default.
-#define WORKAROUND_FOR_MISSING_CR 0
+#define WORKAROUND_FOR_MISSING_CR 1
status_t HTTPStream::receive_line(char *line, size_t size) {
if (mState != CONNECTED) {
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 0d8c3c6..c860c5c 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -38,6 +38,9 @@
namespace android {
+static const uint8_t kNalUnitTypeSeqParamSet = 0x07;
+static const uint8_t kNalUnitTypePicParamSet = 0x08;
+
class MPEG4Writer::Track {
public:
Track(MPEG4Writer *owner, const sp<MediaSource> &source);
@@ -111,6 +114,20 @@
};
List<SttsTableEntry> mSttsTableEntries;
+ // Sequence parameter set or picture parameter set
+ struct AVCParamSet {
+ AVCParamSet(uint16_t length, const uint8_t *data)
+ : mLength(length), mData(data) {}
+
+ uint16_t mLength;
+ const uint8_t *mData;
+ };
+ List<AVCParamSet> mSeqParamSets;
+ List<AVCParamSet> mPicParamSets;
+ uint8_t mProfileIdc;
+ uint8_t mProfileCompatible;
+ uint8_t mLevelIdc;
+
void *mCodecSpecificData;
size_t mCodecSpecificDataSize;
bool mGotAllCodecSpecificData;
@@ -124,8 +141,15 @@
static void *ThreadWrapper(void *me);
void threadEntry();
+ const uint8_t *parseParamSet(
+ const uint8_t *data, size_t length, int type, size_t *paramSetLen);
+
status_t makeAVCCodecSpecificData(
const uint8_t *data, size_t size);
+ status_t copyAVCCodecSpecificData(
+ const uint8_t *data, size_t size);
+ status_t parseAVCCodecSpecificData(
+ const uint8_t *data, size_t size);
// Track authoring progress status
void trackProgressStatus(int64_t timeUs, status_t err = OK);
@@ -1038,6 +1062,174 @@
}
}
+static void getNalUnitType(uint8_t byte, uint8_t* type) {
+ LOGV("getNalUnitType: %d", byte);
+
+ // nal_unit_type: 5-bit unsigned integer
+ *type = (byte & 0x1F);
+}
+
+static const uint8_t *findNextStartCode(
+ const uint8_t *data, size_t length) {
+
+ LOGV("findNextStartCode: %p %d", data, length);
+
+ size_t bytesLeft = length;
+ while (bytesLeft > 4 &&
+ memcmp("\x00\x00\x00\x01", &data[length - bytesLeft], 4)) {
+ --bytesLeft;
+ }
+ if (bytesLeft <= 4) {
+ bytesLeft = 0; // Last parameter set
+ }
+ return &data[length - bytesLeft];
+}
+
+const uint8_t *MPEG4Writer::Track::parseParamSet(
+ const uint8_t *data, size_t length, int type, size_t *paramSetLen) {
+
+ LOGV("parseParamSet");
+ CHECK(type == kNalUnitTypeSeqParamSet ||
+ type == kNalUnitTypePicParamSet);
+
+ const uint8_t *nextStartCode = findNextStartCode(data, length);
+ *paramSetLen = nextStartCode - data;
+ if (*paramSetLen == 0) {
+ LOGE("Param set is malformed, since its length is 0");
+ return NULL;
+ }
+
+ AVCParamSet paramSet(*paramSetLen, data);
+ if (type == kNalUnitTypeSeqParamSet) {
+ if (*paramSetLen < 4) {
+ LOGE("Seq parameter set malformed");
+ return NULL;
+ }
+ if (mSeqParamSets.empty()) {
+ mProfileIdc = data[1];
+ mProfileCompatible = data[2];
+ mLevelIdc = data[3];
+ } else {
+ if (mProfileIdc != data[1] ||
+ mProfileCompatible != data[2] ||
+ mLevelIdc != data[3]) {
+ LOGE("Inconsistent profile/level found in seq parameter sets");
+ return NULL;
+ }
+ }
+ mSeqParamSets.push_back(paramSet);
+ } else {
+ mPicParamSets.push_back(paramSet);
+ }
+ return nextStartCode;
+}
+
+status_t MPEG4Writer::Track::copyAVCCodecSpecificData(
+ const uint8_t *data, size_t size) {
+ LOGV("copyAVCCodecSpecificData");
+
+ // 2 bytes for each of the parameter set length field
+ // plus the 7 bytes for the header
+ if (size < 4 + 7) {
+ LOGE("Codec specific data length too short: %d", size);
+ return ERROR_MALFORMED;
+ }
+
+ mCodecSpecificDataSize = size;
+ mCodecSpecificData = malloc(size);
+ memcpy(mCodecSpecificData, data, size);
+ return OK;
+}
+
+status_t MPEG4Writer::Track::parseAVCCodecSpecificData(
+ const uint8_t *data, size_t size) {
+
+ LOGV("parseAVCCodecSpecificData");
+ // Data starts with a start code.
+ // SPS and PPS are separated with start codes.
+ // Also, SPS must come before PPS
+ uint8_t type = kNalUnitTypeSeqParamSet;
+ bool gotSps = false;
+ bool gotPps = false;
+ const uint8_t *tmp = data;
+ const uint8_t *nextStartCode = data;
+ size_t bytesLeft = size;
+ size_t paramSetLen = 0;
+ mCodecSpecificDataSize = 0;
+ while (bytesLeft > 4 && !memcmp("\x00\x00\x00\x01", tmp, 4)) {
+ getNalUnitType(*(tmp + 4), &type);
+ if (type == kNalUnitTypeSeqParamSet) {
+ if (gotPps) {
+ LOGE("SPS must come before PPS");
+ return ERROR_MALFORMED;
+ }
+ if (!gotSps) {
+ gotSps = true;
+ }
+ nextStartCode = parseParamSet(tmp + 4, bytesLeft - 4, type, ¶mSetLen);
+ } else if (type == kNalUnitTypePicParamSet) {
+ if (!gotSps) {
+ LOGE("SPS must come before PPS");
+ return ERROR_MALFORMED;
+ }
+ if (!gotPps) {
+ gotPps = true;
+ }
+ nextStartCode = parseParamSet(tmp + 4, bytesLeft - 4, type, ¶mSetLen);
+ } else {
+ LOGE("Only SPS and PPS Nal units are expected");
+ return ERROR_MALFORMED;
+ }
+
+ if (nextStartCode == NULL) {
+ return ERROR_MALFORMED;
+ }
+
+ // Move on to find the next parameter set
+ bytesLeft -= nextStartCode - tmp;
+ tmp = nextStartCode;
+ mCodecSpecificDataSize += (2 + paramSetLen);
+ }
+
+ {
+ // Check on the number of seq parameter sets
+ size_t nSeqParamSets = mSeqParamSets.size();
+ if (nSeqParamSets == 0) {
+ LOGE("Cound not find sequence parameter set");
+ return ERROR_MALFORMED;
+ }
+
+ if (nSeqParamSets > 0x1F) {
+ LOGE("Too many seq parameter sets (%d) found", nSeqParamSets);
+ return ERROR_MALFORMED;
+ }
+ }
+
+ {
+ // Check on the number of pic parameter sets
+ size_t nPicParamSets = mPicParamSets.size();
+ if (nPicParamSets == 0) {
+ LOGE("Cound not find picture parameter set");
+ return ERROR_MALFORMED;
+ }
+ if (nPicParamSets > 0xFF) {
+ LOGE("Too many pic parameter sets (%d) found", nPicParamSets);
+ return ERROR_MALFORMED;
+ }
+ }
+
+ {
+ // Check on the profiles
+ // These profiles requires additional parameter set extensions
+ if (mProfileIdc == 100 || mProfileIdc == 110 ||
+ mProfileIdc == 122 || mProfileIdc == 144) {
+ LOGE("Sorry, no support for profile_idc: %d!", mProfileIdc);
+ return BAD_VALUE;
+ }
+ }
+
+ return OK;
+}
status_t MPEG4Writer::Track::makeAVCCodecSpecificData(
const uint8_t *data, size_t size) {
@@ -1048,50 +1240,67 @@
return ERROR_MALFORMED;
}
- if (size < 4 || memcmp("\x00\x00\x00\x01", data, 4)) {
- LOGE("Must start with a start code");
+ if (size < 4) {
+ LOGE("Codec specific data length too short: %d", size);
return ERROR_MALFORMED;
}
- size_t picParamOffset = 4;
- while (picParamOffset + 3 < size
- && memcmp("\x00\x00\x00\x01", &data[picParamOffset], 4)) {
- ++picParamOffset;
+ // Data is in the form of AVCCodecSpecificData
+ if (memcmp("\x00\x00\x00\x01", data, 4)) {
+ return copyAVCCodecSpecificData(data, size);
}
- if (picParamOffset + 3 >= size) {
- LOGE("Could not find start-code for pictureParameterSet");
+ if (parseAVCCodecSpecificData(data, size) != OK) {
return ERROR_MALFORMED;
}
- size_t seqParamSetLength = picParamOffset - 4;
- size_t picParamSetLength = size - picParamOffset - 4;
-
- mCodecSpecificDataSize =
- 6 + 1 + seqParamSetLength + 2 + picParamSetLength + 2;
-
+ // ISO 14496-15: AVC file format
+ mCodecSpecificDataSize += 7; // 7 more bytes in the header
mCodecSpecificData = malloc(mCodecSpecificDataSize);
uint8_t *header = (uint8_t *)mCodecSpecificData;
- header[0] = 1;
- header[1] = 0x42; // profile
- header[2] = 0x80;
- header[3] = 0x1e; // level
+ header[0] = 1; // version
+ header[1] = mProfileIdc; // profile indication
+ header[2] = mProfileCompatible; // profile compatibility
+ header[3] = mLevelIdc;
+ // 6-bit '111111' followed by 2-bit to lengthSizeMinuusOne
#if USE_NALLEN_FOUR
header[4] = 0xfc | 3; // length size == 4 bytes
#else
header[4] = 0xfc | 1; // length size == 2 bytes
#endif
- header[5] = 0xe0 | 1;
- header[6] = seqParamSetLength >> 8;
- header[7] = seqParamSetLength & 0xff;
- memcpy(&header[8], &data[4], seqParamSetLength);
- header += 8 + seqParamSetLength;
- header[0] = 1;
- header[1] = picParamSetLength >> 8;
- header[2] = picParamSetLength & 0xff;
- memcpy(&header[3], &data[picParamOffset + 4], picParamSetLength);
+ // 3-bit '111' followed by 5-bit numSequenceParameterSets
+ int nSequenceParamSets = mSeqParamSets.size();
+ header[5] = 0xe0 | nSequenceParamSets;
+ header += 6;
+ for (List<AVCParamSet>::iterator it = mSeqParamSets.begin();
+ it != mSeqParamSets.end(); ++it) {
+ // 16-bit sequence parameter set length
+ uint16_t seqParamSetLength = it->mLength;
+ header[0] = seqParamSetLength >> 8;
+ header[1] = seqParamSetLength & 0xff;
+
+ // SPS NAL unit (sequence parameter length bytes)
+ memcpy(&header[2], it->mData, seqParamSetLength);
+ header += (2 + seqParamSetLength);
+ }
+
+ // 8-bit nPictureParameterSets
+ int nPictureParamSets = mPicParamSets.size();
+ header[0] = nPictureParamSets;
+ header += 1;
+ for (List<AVCParamSet>::iterator it = mPicParamSets.begin();
+ it != mPicParamSets.end(); ++it) {
+ // 16-bit picture parameter set length
+ uint16_t picParamSetLength = it->mLength;
+ header[0] = picParamSetLength >> 8;
+ header[1] = picParamSetLength & 0xff;
+
+ // PPS Nal unit (picture parameter set length bytes)
+ memcpy(&header[2], it->mData, picParamSetLength);
+ header += (2 + picParamSetLength);
+ }
return OK;
}
@@ -1168,91 +1377,6 @@
mGotAllCodecSpecificData = true;
continue;
- } else if (!mGotAllCodecSpecificData &&
- count == 1 && mIsMPEG4 && mCodecSpecificData == NULL) {
- // The TI mpeg4 encoder does not properly set the
- // codec-specific-data flag.
-
- const uint8_t *data =
- (const uint8_t *)buffer->data() + buffer->range_offset();
-
- const size_t size = buffer->range_length();
-
- size_t offset = 0;
- while (offset + 3 < size) {
- if (data[offset] == 0x00 && data[offset + 1] == 0x00
- && data[offset + 2] == 0x01 && data[offset + 3] == 0xb6) {
- break;
- }
-
- ++offset;
- }
-
- // CHECK(offset + 3 < size);
- if (offset + 3 >= size) {
- // XXX assume the entire first chunk of data is the codec specific
- // data.
- offset = size;
- }
-
- mCodecSpecificDataSize = offset;
- mCodecSpecificData = malloc(offset);
- memcpy(mCodecSpecificData, data, offset);
-
- buffer->set_range(buffer->range_offset() + offset, size - offset);
-
- if (size == offset) {
- buffer->release();
- buffer = NULL;
-
- continue;
- }
-
- mGotAllCodecSpecificData = true;
- } else if (!mGotAllCodecSpecificData && mIsAvc && count < 3) {
- // The TI video encoder does not flag codec specific data
- // as such and also splits up SPS and PPS across two buffers.
-
- const uint8_t *data =
- (const uint8_t *)buffer->data() + buffer->range_offset();
-
- size_t size = buffer->range_length();
-
- CHECK(count == 2 || mCodecSpecificData == NULL);
-
- size_t offset = mCodecSpecificDataSize;
- mCodecSpecificDataSize += size + 4;
- mCodecSpecificData =
- realloc(mCodecSpecificData, mCodecSpecificDataSize);
-
- memcpy((uint8_t *)mCodecSpecificData + offset,
- "\x00\x00\x00\x01", 4);
-
- memcpy((uint8_t *)mCodecSpecificData + offset + 4, data, size);
-
- buffer->release();
- buffer = NULL;
-
- if (count == 2) {
- void *tmp = mCodecSpecificData;
- size = mCodecSpecificDataSize;
- mCodecSpecificData = NULL;
- mCodecSpecificDataSize = 0;
-
- status_t err = makeAVCCodecSpecificData(
- (const uint8_t *)tmp, size);
- free(tmp);
- tmp = NULL;
- CHECK_EQ(OK, err);
-
- mGotAllCodecSpecificData = true;
- }
-
- continue;
- }
-
- if (!mGotAllCodecSpecificData) {
- mGotAllCodecSpecificData = true;
}
// Make a deep copy of the MediaBuffer and Metadata and release
@@ -1753,6 +1877,8 @@
mOwner->writeInt32(samplerate << 16);
if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
mOwner->beginBox("esds");
+ CHECK(mCodecSpecificData);
+ CHECK(mCodecSpecificDataSize > 0);
mOwner->writeInt32(0); // version=0, flags=0
mOwner->writeInt8(0x03); // ES_DescrTag
@@ -1830,6 +1956,8 @@
mOwner->writeInt16(0x18); // depth
mOwner->writeInt16(-1); // predefined
+ CHECK(mCodecSpecificData);
+ CHECK(mCodecSpecificDataSize > 0);
CHECK(23 + mCodecSpecificDataSize < 128);
if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
@@ -1877,6 +2005,8 @@
mOwner->endBox(); // d263
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
+ CHECK(mCodecSpecificData);
+ CHECK(mCodecSpecificDataSize > 0);
mOwner->beginBox("avcC");
mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
mOwner->endBox(); // avcC
diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp
index 39d264c..7648d42 100644
--- a/media/libstagefright/MediaDefs.cpp
+++ b/media/libstagefright/MediaDefs.cpp
@@ -32,6 +32,8 @@
const char *MEDIA_MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
const char *MEDIA_MIMETYPE_AUDIO_QCELP = "audio/qcelp";
const char *MEDIA_MIMETYPE_AUDIO_VORBIS = "audio/vorbis";
+const char *MEDIA_MIMETYPE_AUDIO_G711_ALAW = "audio/g711-alaw";
+const char *MEDIA_MIMETYPE_AUDIO_G711_MLAW = "audio/g711-mlaw";
const char *MEDIA_MIMETYPE_AUDIO_RAW = "audio/raw";
const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mpeg4";
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 4f3bffd..4741b1d 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -26,6 +26,7 @@
#include "include/AMRWBEncoder.h"
#include "include/AVCDecoder.h"
#include "include/AVCEncoder.h"
+#include "include/G711Decoder.h"
#include "include/M4vH263Decoder.h"
#include "include/M4vH263Encoder.h"
#include "include/MP3Decoder.h"
@@ -77,6 +78,7 @@
FACTORY_CREATE(AMRWBDecoder)
FACTORY_CREATE(AACDecoder)
FACTORY_CREATE(AVCDecoder)
+FACTORY_CREATE(G711Decoder)
FACTORY_CREATE(M4vH263Decoder)
FACTORY_CREATE(VorbisDecoder)
FACTORY_CREATE(VPXDecoder)
@@ -124,6 +126,7 @@
FACTORY_REF(AMRWBDecoder)
FACTORY_REF(AACDecoder)
FACTORY_REF(AVCDecoder)
+ FACTORY_REF(G711Decoder)
FACTORY_REF(M4vH263Decoder)
FACTORY_REF(VorbisDecoder)
FACTORY_REF(VPXDecoder)
@@ -155,6 +158,8 @@
{ MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.decode" },
{ MEDIA_MIMETYPE_AUDIO_AAC, "AACDecoder" },
// { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.PV.aacdec" },
+ { MEDIA_MIMETYPE_AUDIO_G711_ALAW, "G711Decoder" },
+ { MEDIA_MIMETYPE_AUDIO_G711_MLAW, "G711Decoder" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.decoder.mpeg4" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.decoder.mpeg4" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.Decoder" },
@@ -1680,6 +1685,14 @@
MediaBuffer *buffer = info->mMediaBuffer;
+ if (msg.u.extended_buffer_data.range_offset
+ + msg.u.extended_buffer_data.range_length
+ > buffer->size()) {
+ CODEC_LOGE(
+ "Codec lied about its buffer size requirements, "
+ "sending a buffer larger than the originally "
+ "advertised size in FILL_BUFFER_DONE!");
+ }
buffer->set_range(
msg.u.extended_buffer_data.range_offset,
msg.u.extended_buffer_data.range_length);
diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp
index 39b1b96..8d820c0 100644
--- a/media/libstagefright/WAVExtractor.cpp
+++ b/media/libstagefright/WAVExtractor.cpp
@@ -31,7 +31,11 @@
namespace android {
-static uint16_t WAVE_FORMAT_PCM = 1;
+enum {
+ WAVE_FORMAT_PCM = 1,
+ WAVE_FORMAT_ALAW = 6,
+ WAVE_FORMAT_MULAW = 7,
+};
static uint32_t U32_LE_AT(const uint8_t *ptr) {
return ptr[3] << 24 | ptr[2] << 16 | ptr[1] << 8 | ptr[0];
@@ -45,6 +49,7 @@
WAVSource(
const sp<DataSource> &dataSource,
const sp<MetaData> &meta,
+ uint16_t waveFormat,
int32_t bitsPerSample,
off_t offset, size_t size);
@@ -63,6 +68,7 @@
sp<DataSource> mDataSource;
sp<MetaData> mMeta;
+ uint16_t mWaveFormat;
int32_t mSampleRate;
int32_t mNumChannels;
int32_t mBitsPerSample;
@@ -108,7 +114,7 @@
return new WAVSource(
mDataSource, mTrackMeta,
- mBitsPerSample, mDataOffset, mDataSize);
+ mWaveFormat, mBitsPerSample, mDataOffset, mDataSize);
}
sp<MetaData> WAVExtractor::getTrackMetaData(
@@ -160,8 +166,10 @@
return NO_INIT;
}
- uint16_t format = U16_LE_AT(formatSpec);
- if (format != WAVE_FORMAT_PCM) {
+ mWaveFormat = U16_LE_AT(formatSpec);
+ if (mWaveFormat != WAVE_FORMAT_PCM
+ && mWaveFormat != WAVE_FORMAT_ALAW
+ && mWaveFormat != WAVE_FORMAT_MULAW) {
return ERROR_UNSUPPORTED;
}
@@ -178,9 +186,17 @@
mBitsPerSample = U16_LE_AT(&formatSpec[14]);
- if (mBitsPerSample != 8 && mBitsPerSample != 16
- && mBitsPerSample != 24) {
- return ERROR_UNSUPPORTED;
+ if (mWaveFormat == WAVE_FORMAT_PCM) {
+ if (mBitsPerSample != 8 && mBitsPerSample != 16
+ && mBitsPerSample != 24) {
+ return ERROR_UNSUPPORTED;
+ }
+ } else {
+ CHECK(mWaveFormat == WAVE_FORMAT_MULAW
+ || mWaveFormat == WAVE_FORMAT_ALAW);
+ if (mBitsPerSample != 8) {
+ return ERROR_UNSUPPORTED;
+ }
}
mValidFormat = true;
@@ -190,7 +206,23 @@
mDataSize = chunkSize;
mTrackMeta = new MetaData;
- mTrackMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
+
+ switch (mWaveFormat) {
+ case WAVE_FORMAT_PCM:
+ mTrackMeta->setCString(
+ kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
+ break;
+ case WAVE_FORMAT_ALAW:
+ mTrackMeta->setCString(
+ kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_G711_ALAW);
+ break;
+ default:
+ CHECK_EQ(mWaveFormat, WAVE_FORMAT_MULAW);
+ mTrackMeta->setCString(
+ kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_G711_MLAW);
+ break;
+ }
+
mTrackMeta->setInt32(kKeyChannelCount, mNumChannels);
mTrackMeta->setInt32(kKeySampleRate, mSampleRate);
@@ -217,10 +249,12 @@
WAVSource::WAVSource(
const sp<DataSource> &dataSource,
const sp<MetaData> &meta,
+ uint16_t waveFormat,
int32_t bitsPerSample,
off_t offset, size_t size)
: mDataSource(dataSource),
mMeta(meta),
+ mWaveFormat(waveFormat),
mSampleRate(0),
mNumChannels(0),
mBitsPerSample(bitsPerSample),
@@ -312,43 +346,45 @@
buffer->set_range(0, n);
- if (mBitsPerSample == 8) {
- // Convert 8-bit unsigned samples to 16-bit signed.
+ if (mWaveFormat == WAVE_FORMAT_PCM) {
+ if (mBitsPerSample == 8) {
+ // Convert 8-bit unsigned samples to 16-bit signed.
- MediaBuffer *tmp;
- CHECK_EQ(mGroup->acquire_buffer(&tmp), OK);
+ MediaBuffer *tmp;
+ CHECK_EQ(mGroup->acquire_buffer(&tmp), OK);
- // The new buffer holds the sample number of samples, but each
- // one is 2 bytes wide.
- tmp->set_range(0, 2 * n);
+ // The new buffer holds the sample number of samples, but each
+ // one is 2 bytes wide.
+ tmp->set_range(0, 2 * n);
- int16_t *dst = (int16_t *)tmp->data();
- const uint8_t *src = (const uint8_t *)buffer->data();
- while (n-- > 0) {
- *dst++ = ((int16_t)(*src) - 128) * 256;
- ++src;
+ int16_t *dst = (int16_t *)tmp->data();
+ const uint8_t *src = (const uint8_t *)buffer->data();
+ while (n-- > 0) {
+ *dst++ = ((int16_t)(*src) - 128) * 256;
+ ++src;
+ }
+
+ buffer->release();
+ buffer = tmp;
+ } else if (mBitsPerSample == 24) {
+ // Convert 24-bit signed samples to 16-bit signed.
+
+ const uint8_t *src =
+ (const uint8_t *)buffer->data() + buffer->range_offset();
+ int16_t *dst = (int16_t *)src;
+
+ size_t numSamples = buffer->range_length() / 3;
+ for (size_t i = 0; i < numSamples; ++i) {
+ int32_t x = (int32_t)(src[0] | src[1] << 8 | src[2] << 16);
+ x = (x << 8) >> 8; // sign extension
+
+ x = x >> 8;
+ *dst++ = (int16_t)x;
+ src += 3;
+ }
+
+ buffer->set_range(buffer->range_offset(), 2 * numSamples);
}
-
- buffer->release();
- buffer = tmp;
- } else if (mBitsPerSample == 24) {
- // Convert 24-bit signed samples to 16-bit signed.
-
- const uint8_t *src =
- (const uint8_t *)buffer->data() + buffer->range_offset();
- int16_t *dst = (int16_t *)src;
-
- size_t numSamples = buffer->range_length() / 3;
- for (size_t i = 0; i < numSamples; ++i) {
- int32_t x = (int32_t)(src[0] | src[1] << 8 | src[2] << 16);
- x = (x << 8) >> 8; // sign extension
-
- x = x >> 8;
- *dst++ = (int16_t)x;
- src += 3;
- }
-
- buffer->set_range(buffer->range_offset(), 2 * numSamples);
}
size_t bytesPerSample = mBitsPerSample >> 3;
diff --git a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
index d5eb156..6e74279 100644
--- a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
+++ b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
@@ -337,14 +337,18 @@
MediaBuffer *outputBuffer;
CHECK_EQ(OK, mGroup->acquire_buffer(&outputBuffer));
- uint8_t *outPtr = (uint8_t *) outputBuffer->data();
- uint32_t dataLength = outputBuffer->size();
+
+ // Add 4 bytes for the start code 0x00000001
+ uint8_t *outPtr = (uint8_t *) outputBuffer->data() + 4;
+ uint32_t dataLength = outputBuffer->size() - 4;
int32_t type;
AVCEnc_Status encoderStatus = AVCENC_SUCCESS;
- // Return SPS and PPS for the first two buffers
- if (!mSpsPpsHeaderReceived) {
+ // Combine SPS and PPS and place them in the very first output buffer
+ // SPS and PPS are separated by start code 0x00000001
+ // Assume that we have exactly one SPS and exactly one PPS.
+ while (!mSpsPpsHeaderReceived && mNumInputFrames <= 0) {
encoderStatus = PVAVCEncodeNAL(mHandle, outPtr, &dataLength, &type);
if (encoderStatus == AVCENC_WRONG_STATE) {
mSpsPpsHeaderReceived = true;
@@ -352,11 +356,22 @@
} else {
switch (type) {
case AVC_NALTYPE_SPS:
- case AVC_NALTYPE_PPS:
- LOGV("%s received",
- (type == AVC_NALTYPE_SPS)? "SPS": "PPS");
++mNumInputFrames;
- outputBuffer->set_range(0, dataLength);
+ memcpy(outputBuffer->data(), "\x00\x00\x00\x01", 4);
+ outputBuffer->set_range(0, dataLength + 4);
+ outPtr += (dataLength + 4); // 4 bytes for next start code
+ dataLength = outputBuffer->size() -
+ (outputBuffer->range_length() + 4);
+ break;
+ case AVC_NALTYPE_PPS:
+ ++mNumInputFrames;
+ memcpy(((uint8_t *) outputBuffer->data()) +
+ outputBuffer->range_length(),
+ "\x00\x00\x00\x01", 4);
+ outputBuffer->set_range(0,
+ dataLength + outputBuffer->range_length() + 4);
+ outputBuffer->meta_data()->setInt32(kKeyIsCodecConfig, 1);
+ outputBuffer->meta_data()->setInt64(kKeyTime, 0);
*out = outputBuffer;
return OK;
default:
@@ -376,8 +391,18 @@
if (err != OK) {
LOGE("Failed to read input video frame: %d", err);
outputBuffer->release();
+ mInputBuffer->release();
+ mInputBuffer = NULL;
return err;
}
+
+ if (mInputBuffer->size() - ((mVideoWidth * mVideoHeight * 3) >> 1) != 0) {
+ outputBuffer->release();
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ return UNKNOWN_ERROR;
+ }
+
int64_t timeUs;
CHECK(mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
outputBuffer->meta_data()->setInt64(kKeyTime, timeUs);
diff --git a/media/libstagefright/codecs/g711/Android.mk b/media/libstagefright/codecs/g711/Android.mk
new file mode 100644
index 0000000..2e431205
--- /dev/null
+++ b/media/libstagefright/codecs/g711/Android.mk
@@ -0,0 +1,4 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/media/libstagefright/codecs/g711/dec/Android.mk b/media/libstagefright/codecs/g711/dec/Android.mk
new file mode 100644
index 0000000..cfb9fe4
--- /dev/null
+++ b/media/libstagefright/codecs/g711/dec/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ G711Decoder.cpp
+
+LOCAL_C_INCLUDES := \
+ frameworks/base/media/libstagefright/include \
+
+LOCAL_MODULE := libstagefright_g711dec
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/media/libstagefright/codecs/g711/dec/G711Decoder.cpp b/media/libstagefright/codecs/g711/dec/G711Decoder.cpp
new file mode 100644
index 0000000..4414e4e
--- /dev/null
+++ b/media/libstagefright/codecs/g711/dec/G711Decoder.cpp
@@ -0,0 +1,213 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "G711Decoder"
+#include <utils/Log.h>
+
+#include "G711Decoder.h"
+
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+
+static const size_t kMaxNumSamplesPerFrame = 16384;
+
+namespace android {
+
+G711Decoder::G711Decoder(const sp<MediaSource> &source)
+ : mSource(source),
+ mStarted(false),
+ mBufferGroup(NULL) {
+}
+
+G711Decoder::~G711Decoder() {
+ if (mStarted) {
+ stop();
+ }
+}
+
+status_t G711Decoder::start(MetaData *params) {
+ CHECK(!mStarted);
+
+ const char *mime;
+ CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
+
+ mIsMLaw = false;
+ if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_G711_MLAW)) {
+ mIsMLaw = true;
+ } else if (strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_G711_ALAW)) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ mBufferGroup = new MediaBufferGroup;
+ mBufferGroup->add_buffer(
+ new MediaBuffer(kMaxNumSamplesPerFrame * sizeof(int16_t)));
+
+ mSource->start();
+
+ mStarted = true;
+
+ return OK;
+}
+
+status_t G711Decoder::stop() {
+ CHECK(mStarted);
+
+ delete mBufferGroup;
+ mBufferGroup = NULL;
+
+ mSource->stop();
+
+ mStarted = false;
+
+ return OK;
+}
+
+sp<MetaData> G711Decoder::getFormat() {
+ sp<MetaData> srcFormat = mSource->getFormat();
+
+ int32_t numChannels;
+ int32_t sampleRate;
+
+ CHECK(srcFormat->findInt32(kKeyChannelCount, &numChannels));
+ CHECK(srcFormat->findInt32(kKeySampleRate, &sampleRate));
+
+ sp<MetaData> meta = new MetaData;
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
+ meta->setInt32(kKeyChannelCount, numChannels);
+ meta->setInt32(kKeySampleRate, sampleRate);
+
+ int64_t durationUs;
+ if (srcFormat->findInt64(kKeyDuration, &durationUs)) {
+ meta->setInt64(kKeyDuration, durationUs);
+ }
+
+ meta->setCString(kKeyDecoderComponent, "G711Decoder");
+
+ return meta;
+}
+
+status_t G711Decoder::read(
+ MediaBuffer **out, const ReadOptions *options) {
+ status_t err;
+
+ *out = NULL;
+
+ int64_t seekTimeUs;
+ ReadOptions::SeekMode mode;
+ if (options && options->getSeekTo(&seekTimeUs, &mode)) {
+ CHECK(seekTimeUs >= 0);
+ } else {
+ seekTimeUs = -1;
+ }
+
+ MediaBuffer *inBuffer;
+ err = mSource->read(&inBuffer, options);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (inBuffer->range_length() > kMaxNumSamplesPerFrame) {
+ LOGE("input buffer too large (%d).", inBuffer->range_length());
+
+ inBuffer->release();
+ inBuffer = NULL;
+
+ return ERROR_UNSUPPORTED;
+ }
+
+ int64_t timeUs;
+ CHECK(inBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
+
+ const uint8_t *inputPtr =
+ (const uint8_t *)inBuffer->data() + inBuffer->range_offset();
+
+ MediaBuffer *outBuffer;
+ CHECK_EQ(mBufferGroup->acquire_buffer(&outBuffer), OK);
+
+ if (mIsMLaw) {
+ DecodeMLaw(
+ static_cast<int16_t *>(outBuffer->data()),
+ inputPtr, inBuffer->range_length());
+ } else {
+ DecodeALaw(
+ static_cast<int16_t *>(outBuffer->data()),
+ inputPtr, inBuffer->range_length());
+ }
+
+ // Each 8-bit byte is converted into a 16-bit sample.
+ outBuffer->set_range(0, inBuffer->range_length() * 2);
+
+ outBuffer->meta_data()->setInt64(kKeyTime, timeUs);
+
+ inBuffer->release();
+ inBuffer = NULL;
+
+ *out = outBuffer;
+
+ return OK;
+}
+
+// static
+void G711Decoder::DecodeALaw(
+ int16_t *out, const uint8_t *in, size_t inSize) {
+ while (inSize-- > 0) {
+ int32_t x = *in++;
+
+ int32_t ix = x ^ 0x55;
+ ix &= 0x7f;
+
+ int32_t iexp = ix >> 4;
+ int32_t mant = ix & 0x0f;
+
+ if (iexp > 0) {
+ mant += 16;
+ }
+
+ mant = (mant << 4) + 8;
+
+ if (iexp > 1) {
+ mant = mant << (iexp - 1);
+ }
+
+ *out++ = (x > 127) ? mant : -mant;
+ }
+}
+
+// static
+void G711Decoder::DecodeMLaw(
+ int16_t *out, const uint8_t *in, size_t inSize) {
+ while (inSize-- > 0) {
+ int32_t x = *in++;
+
+ int32_t mantissa = ~x;
+ int32_t exponent = (mantissa >> 4) & 7;
+ int32_t segment = exponent + 1;
+ mantissa &= 0x0f;
+
+ int32_t step = 4 << segment;
+
+ int32_t abs = (0x80l << exponent) + step * mantissa + step / 2 - 4 * 33;
+
+ *out++ = (x < 0x80) ? -abs : abs;
+ }
+}
+
+} // namespace android
diff --git a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
index 5002442e..1bef0e9 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
+++ b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
@@ -292,8 +292,18 @@
if (OK != mSource->read(&mInputBuffer, options)) {
LOGE("Failed to read from data source");
outputBuffer->release();
+ mInputBuffer->release();
+ mInputBuffer = NULL;
return UNKNOWN_ERROR;
}
+
+ if (mInputBuffer->size() - ((mVideoWidth * mVideoHeight * 3) >> 1) != 0) {
+ outputBuffer->release();
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ return UNKNOWN_ERROR;
+ }
+
int64_t timeUs;
CHECK(mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
if (mNextModTimeUs > timeUs) {
diff --git a/media/libstagefright/include/G711Decoder.h b/media/libstagefright/include/G711Decoder.h
new file mode 100644
index 0000000..8b5143a
--- /dev/null
+++ b/media/libstagefright/include/G711Decoder.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#ifndef G711_DECODER_H_
+
+#define G711_DECODER_H_
+
+#include <media/stagefright/MediaSource.h>
+
+namespace android {
+
+struct MediaBufferGroup;
+
+struct G711Decoder : public MediaSource {
+ G711Decoder(const sp<MediaSource> &source);
+
+ virtual status_t start(MetaData *params);
+ virtual status_t stop();
+
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options);
+
+protected:
+ virtual ~G711Decoder();
+
+private:
+ sp<MediaSource> mSource;
+ bool mStarted;
+ bool mIsMLaw;
+
+ MediaBufferGroup *mBufferGroup;
+
+ static void DecodeALaw(int16_t *out, const uint8_t *in, size_t inSize);
+ static void DecodeMLaw(int16_t *out, const uint8_t *in, size_t inSize);
+
+ G711Decoder(const G711Decoder &);
+ G711Decoder &operator=(const G711Decoder &);
+};
+
+} // namespace android
+
+#endif // G711_DECODER_H_
diff --git a/media/libstagefright/include/WAVExtractor.h b/media/libstagefright/include/WAVExtractor.h
index 9384942..3e847b9 100644
--- a/media/libstagefright/include/WAVExtractor.h
+++ b/media/libstagefright/include/WAVExtractor.h
@@ -43,6 +43,7 @@
sp<DataSource> mDataSource;
status_t mInitCheck;
bool mValidFormat;
+ uint16_t mWaveFormat;
uint16_t mNumChannels;
uint32_t mSampleRate;
uint16_t mBitsPerSample;
diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp
index a577704..395cd28 100644
--- a/media/libstagefright/rtsp/APacketSource.cpp
+++ b/media/libstagefright/rtsp/APacketSource.cpp
@@ -356,24 +356,10 @@
if (!mBuffers.empty()) {
const sp<ABuffer> buffer = *mBuffers.begin();
- uint64_t ntpTime;
- CHECK(buffer->meta()->findInt64(
- "ntp-time", (int64_t *)&ntpTime));
-
MediaBuffer *mediaBuffer = new MediaBuffer(buffer->size());
- mediaBuffer->meta_data()->setInt64(kKeyNTPTime, ntpTime);
- if (mFirstAccessUnit) {
- mFirstAccessUnit = false;
- mFirstAccessUnitNTP = ntpTime;
- }
- if (ntpTime > mFirstAccessUnitNTP) {
- ntpTime -= mFirstAccessUnitNTP;
- } else {
- ntpTime = 0;
- }
-
- int64_t timeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
+ int64_t timeUs;
+ CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs);
@@ -390,10 +376,29 @@
void APacketSource::queueAccessUnit(const sp<ABuffer> &buffer) {
int32_t damaged;
if (buffer->meta()->findInt32("damaged", &damaged) && damaged) {
- // LOG(VERBOSE) << "discarding damaged AU";
+ LOG(INFO) << "discarding damaged AU";
return;
}
+ uint64_t ntpTime;
+ CHECK(buffer->meta()->findInt64(
+ "ntp-time", (int64_t *)&ntpTime));
+
+ if (mFirstAccessUnit) {
+ mFirstAccessUnit = false;
+ mFirstAccessUnitNTP = ntpTime;
+ }
+
+ if (ntpTime > mFirstAccessUnitNTP) {
+ ntpTime -= mFirstAccessUnitNTP;
+ } else {
+ ntpTime = 0;
+ }
+
+ int64_t timeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
+
+ buffer->meta()->setInt64("timeUs", timeUs);
+
Mutex::Autolock autoLock(mLock);
mBuffers.push_back(buffer);
mCondition.signal();
diff --git a/media/libstagefright/rtsp/ARTPConnection.cpp b/media/libstagefright/rtsp/ARTPConnection.cpp
index 5bd306b..469af3e 100644
--- a/media/libstagefright/rtsp/ARTPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTPConnection.cpp
@@ -28,8 +28,6 @@
#include <arpa/inet.h>
#include <sys/socket.h>
-#define IGNORE_RTCP_TIME 0
-
namespace android {
static const size_t kMaxUDPSize = 1500;
@@ -61,8 +59,9 @@
struct sockaddr_in mRemoteRTCPAddr;
};
-ARTPConnection::ARTPConnection()
- : mPollEventPending(false),
+ARTPConnection::ARTPConnection(uint32_t flags)
+ : mFlags(flags),
+ mPollEventPending(false),
mLastReceiverReportTimeUs(-1) {
}
@@ -280,7 +279,10 @@
sp<ARTPSource> source = s->mSources.valueAt(i);
source->addReceiverReport(buffer);
- source->addFIR(buffer);
+
+ if (mFlags & kRegularlyRequestFIR) {
+ source->addFIR(buffer);
+ }
}
if (buffer->size() > 0) {
@@ -405,13 +407,11 @@
buffer->setInt32Data(u16at(&data[2]));
buffer->setRange(payloadOffset, size - payloadOffset);
-#if IGNORE_RTCP_TIME
- if (!source->timeEstablished()) {
+ if ((mFlags & kFakeTimestamps) && !source->timeEstablished()) {
source->timeUpdate(rtpTime, 0);
- source->timeUpdate(rtpTime + 20, 0x100000000ll);
+ source->timeUpdate(rtpTime + 90000, 0x100000000ll);
CHECK(source->timeEstablished());
}
-#endif
source->processRTPPacket(buffer);
@@ -533,9 +533,9 @@
sp<ARTPSource> source = findSource(s, id);
-#if !IGNORE_RTCP_TIME
- source->timeUpdate(rtpTime, ntpTime);
-#endif
+ if ((mFlags & kFakeTimestamps) == 0) {
+ source->timeUpdate(rtpTime, ntpTime);
+ }
return 0;
}
diff --git a/media/libstagefright/rtsp/ARTPConnection.h b/media/libstagefright/rtsp/ARTPConnection.h
index 49839ad..c535199 100644
--- a/media/libstagefright/rtsp/ARTPConnection.h
+++ b/media/libstagefright/rtsp/ARTPConnection.h
@@ -28,7 +28,12 @@
struct ASessionDescription;
struct ARTPConnection : public AHandler {
- ARTPConnection();
+ enum Flags {
+ kFakeTimestamps = 1,
+ kRegularlyRequestFIR = 2,
+ };
+
+ ARTPConnection(uint32_t flags = 0);
void addStream(
int rtpSocket, int rtcpSocket,
@@ -56,6 +61,8 @@
static const int64_t kSelectTimeoutUs;
+ uint32_t mFlags;
+
struct StreamInfo;
List<StreamInfo> mStreams;
diff --git a/media/libstagefright/rtsp/ARTPSession.cpp b/media/libstagefright/rtsp/ARTPSession.cpp
index 0e0f45a..e082078 100644
--- a/media/libstagefright/rtsp/ARTPSession.cpp
+++ b/media/libstagefright/rtsp/ARTPSession.cpp
@@ -40,7 +40,10 @@
mDesc = desc;
- mRTPConn = new ARTPConnection;
+ mRTPConn = new ARTPConnection(
+ ARTPConnection::kFakeTimestamps
+ | ARTPConnection::kRegularlyRequestFIR);
+
looper()->registerHandler(mRTPConn);
for (size_t i = 1; i < mDesc->countTracks(); ++i) {
diff --git a/media/libstagefright/rtsp/ARTPSource.cpp b/media/libstagefright/rtsp/ARTPSource.cpp
index e08183e..225f6e8 100644
--- a/media/libstagefright/rtsp/ARTPSource.cpp
+++ b/media/libstagefright/rtsp/ARTPSource.cpp
@@ -98,7 +98,7 @@
mNTPTime[mNumTimes] = ntpTime;
mRTPTime[mNumTimes++] = rtpTime;
- if (mNumTimes == 2) {
+ if (timeEstablished()) {
for (List<sp<ABuffer> >::iterator it = mQueue.begin();
it != mQueue.end(); ++it) {
sp<AMessage> meta = (*it)->meta();
@@ -112,13 +112,6 @@
}
bool ARTPSource::queuePacket(const sp<ABuffer> &buffer) {
-#if 1
- if (mNumTimes != 2) {
- // Drop incoming packets until we've established a time base.
- return false;
- }
-#endif
-
uint32_t seqNum = (uint32_t)buffer->int32Data();
if (mNumTimes == 2) {
diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp
index c159e20..9bfd00f 100644
--- a/media/mtp/MtpDataPacket.cpp
+++ b/media/mtp/MtpDataPacket.cpp
@@ -266,6 +266,13 @@
putUInt16(*values++);
}
+void MtpDataPacket::putAUInt16(const UInt16List* values) {
+ size_t count = (values ? values->size() : 0);
+ putUInt32(count);
+ for (size_t i = 0; i < count; i++)
+ putUInt16((*values)[i]);
+}
+
void MtpDataPacket::putAInt32(const int32_t* values, int count) {
putUInt32(count);
for (int i = 0; i < count; i++)
diff --git a/media/mtp/MtpDataPacket.h b/media/mtp/MtpDataPacket.h
index e8314d7..b458286 100644
--- a/media/mtp/MtpDataPacket.h
+++ b/media/mtp/MtpDataPacket.h
@@ -74,6 +74,7 @@
void putAUInt8(const uint8_t* values, int count);
void putAInt16(const int16_t* values, int count);
void putAUInt16(const uint16_t* values, int count);
+ void putAUInt16(const UInt16List* values);
void putAInt32(const int32_t* values, int count);
void putAUInt32(const uint32_t* values, int count);
void putAUInt32(const UInt32List* list);
diff --git a/media/mtp/MtpDatabase.h b/media/mtp/MtpDatabase.h
index 02bb0d9..17823df 100644
--- a/media/mtp/MtpDatabase.h
+++ b/media/mtp/MtpDatabase.h
@@ -51,6 +51,13 @@
MtpObjectFormat format,
MtpObjectHandle parent) = 0;
+ // callee should delete[] the results from these
+ // results can be NULL
+ virtual MtpObjectFormatList* getSupportedPlaybackFormats() = 0;
+ virtual MtpObjectFormatList* getSupportedCaptureFormats() = 0;
+ virtual MtpObjectPropertyList* getSupportedObjectProperties(MtpObjectFormat format) = 0;
+ virtual MtpDevicePropertyList* getSupportedDeviceProperties() = 0;
+
virtual MtpResponseCode getObjectProperty(MtpObjectHandle handle,
MtpObjectProperty property,
MtpDataPacket& packet) = 0;
diff --git a/media/mtp/MtpDebug.h b/media/mtp/MtpDebug.h
index 3935d33..5b53e31 100644
--- a/media/mtp/MtpDebug.h
+++ b/media/mtp/MtpDebug.h
@@ -17,7 +17,7 @@
#ifndef _MTP_DEBUG_H
#define _MTP_DEBUG_H
-#define LOG_NDEBUG 0
+// #define LOG_NDEBUG 0
#include <utils/Log.h>
#include "MtpTypes.h"
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index 082d924..30abfb8 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -78,46 +78,6 @@
MTP_EVENT_OBJECT_REMOVED,
};
-static const MtpObjectProperty kSupportedObjectProperties[] = {
- MTP_PROPERTY_STORAGE_ID,
- MTP_PROPERTY_OBJECT_FORMAT,
- MTP_PROPERTY_OBJECT_SIZE,
- MTP_PROPERTY_OBJECT_FILE_NAME,
- MTP_PROPERTY_PARENT_OBJECT,
-};
-
-static const MtpObjectFormat kSupportedPlaybackFormats[] = {
- // MTP_FORMAT_UNDEFINED,
- MTP_FORMAT_ASSOCIATION,
- // MTP_FORMAT_TEXT,
- // MTP_FORMAT_HTML,
- MTP_FORMAT_MP3,
- //MTP_FORMAT_AVI,
- MTP_FORMAT_MPEG,
- // MTP_FORMAT_ASF,
- MTP_FORMAT_EXIF_JPEG,
- MTP_FORMAT_TIFF_EP,
- // MTP_FORMAT_BMP,
- MTP_FORMAT_GIF,
- MTP_FORMAT_JFIF,
- MTP_FORMAT_PNG,
- MTP_FORMAT_TIFF,
- MTP_FORMAT_WMA,
- MTP_FORMAT_OGG,
- MTP_FORMAT_AAC,
- // MTP_FORMAT_FLAC,
- // MTP_FORMAT_WMV,
- MTP_FORMAT_MP4_CONTAINER,
- MTP_FORMAT_MP2,
- MTP_FORMAT_3GP_CONTAINER,
- // MTP_FORMAT_ABSTRACT_AUDIO_ALBUM,
- MTP_FORMAT_ABSTRACT_AV_PLAYLIST,
- MTP_FORMAT_WPL_PLAYLIST,
- MTP_FORMAT_M3U_PLAYLIST,
- // MTP_FORMAT_MPL_PLAYLIST,
- MTP_FORMAT_PLS_PLAYLIST,
-};
-
MtpServer::MtpServer(int fd, MtpDatabase* database,
int fileGroup, int filePerm, int directoryPerm)
: mFD(fd),
@@ -354,6 +314,10 @@
MtpStringBuffer string;
char prop_value[PROPERTY_VALUE_MAX];
+ MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats();
+ MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats();
+ MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties();
+
// fill in device info
mData.putUInt16(MTP_STANDARD_VERSION);
mData.putUInt32(6); // MTP Vendor Extension ID
@@ -365,10 +329,9 @@
sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
mData.putAUInt16(kSupportedEventCodes,
sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
- mData.putEmptyArray(); // Device Properties Supported
- mData.putEmptyArray(); // Capture Formats
- mData.putAUInt16(kSupportedPlaybackFormats,
- sizeof(kSupportedPlaybackFormats) / sizeof(uint16_t)); // Playback Formats
+ mData.putAUInt16(deviceProperties); // Device Properties Supported
+ mData.putAUInt16(captureFormats); // Capture Formats
+ mData.putAUInt16(playbackFormats); // Playback Formats
// FIXME
string.set("Google, Inc.");
mData.putString(string); // Manufacturer
@@ -383,6 +346,10 @@
string.set(prop_value);
mData.putString(string); // Serial Number
+ delete playbackFormats;
+ delete captureFormats;
+ delete deviceProperties;
+
return MTP_RESPONSE_OK;
}
@@ -443,8 +410,9 @@
if (!mSessionOpen)
return MTP_RESPONSE_SESSION_NOT_OPEN;
MtpObjectFormat format = mRequest.getParameter(1);
- mData.putAUInt16(kSupportedObjectProperties,
- sizeof(kSupportedObjectProperties) / sizeof(uint16_t));
+ MtpDevicePropertyList* properties = mDatabase->getSupportedObjectProperties(format);
+ mData.putAUInt16(properties);
+ delete properties;
return MTP_RESPONSE_OK;
}
diff --git a/media/mtp/MtpTypes.h b/media/mtp/MtpTypes.h
index 2a895a7..7e3c009 100644
--- a/media/mtp/MtpTypes.h
+++ b/media/mtp/MtpTypes.h
@@ -78,6 +78,7 @@
typedef Vector<int32_t> Int32List;
typedef Vector<int64_t> Int64List;
+typedef UInt16List MtpObjectPropertyList;
typedef UInt16List MtpDevicePropertyList;
typedef UInt16List MtpObjectFormatList;
typedef UInt32List MtpObjectHandleList;
diff --git a/media/tests/MediaFrameworkTest/AndroidManifest.xml b/media/tests/MediaFrameworkTest/AndroidManifest.xml
index 246f9fc..f70a145 100644
--- a/media/tests/MediaFrameworkTest/AndroidManifest.xml
+++ b/media/tests/MediaFrameworkTest/AndroidManifest.xml
@@ -51,4 +51,9 @@
android:label="MediaRecorder stress tests InstrumentationRunner">
</instrumentation>
+ <instrumentation android:name=".MediaFrameworkPowerTestRunner"
+ android:targetPackage="com.android.mediaframeworktest"
+ android:label="Media Power tests InstrumentationRunner">
+ </instrumentation>
+
</manifest>
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPowerTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPowerTestRunner.java
new file mode 100755
index 0000000..34db4db
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPowerTestRunner.java
@@ -0,0 +1,50 @@
+/*
+ * 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.mediaframeworktest;
+
+import com.android.mediaframeworktest.power.MediaPlayerPowerTest;
+
+import junit.framework.TestSuite;
+
+import android.test.InstrumentationTestRunner;
+import android.test.InstrumentationTestSuite;
+
+
+/**
+ * Instrumentation Test Runner for all MediaPlayer tests.
+ *
+ * Running all tests:
+ *
+ * adb shell am instrument \
+ * -w com.android.mediaframeworktest/.MediaFrameworkPowerTestRunner
+ */
+
+public class MediaFrameworkPowerTestRunner extends InstrumentationTestRunner {
+
+ @Override
+ public TestSuite getAllTests() {
+ TestSuite suite = new InstrumentationTestSuite(this);
+ suite.addTestSuite(MediaPlayerPowerTest.class);
+ return suite;
+ }
+
+ @Override
+ public ClassLoader getLoader() {
+ return MediaFrameworkPowerTestRunner.class.getClassLoader();
+ }
+}
+
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
index 3e33951..c7f461e 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
@@ -25,6 +25,11 @@
import com.android.mediaframeworktest.functional.SimTonesTest;
import com.android.mediaframeworktest.functional.MediaPlayerInvokeTest;
import com.android.mediaframeworktest.functional.MediaAudioManagerTest;
+import com.android.mediaframeworktest.functional.MediaAudioEffectTest;
+import com.android.mediaframeworktest.functional.MediaBassBoostTest;
+import com.android.mediaframeworktest.functional.MediaEqualizerTest;
+import com.android.mediaframeworktest.functional.MediaVirtualizerTest;
+import com.android.mediaframeworktest.functional.MediaVisualizerTest;
import junit.framework.TestSuite;
import android.test.InstrumentationTestRunner;
@@ -55,6 +60,11 @@
suite.addTestSuite(MediaMimeTest.class);
suite.addTestSuite(MediaPlayerInvokeTest.class);
suite.addTestSuite(MediaAudioManagerTest.class);
+ suite.addTestSuite(MediaAudioEffectTest.class);
+ suite.addTestSuite(MediaBassBoostTest.class);
+ suite.addTestSuite(MediaEqualizerTest.class);
+ suite.addTestSuite(MediaVirtualizerTest.class);
+ suite.addTestSuite(MediaVisualizerTest.class);
return suite;
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
index 9a48c92..ca6e999 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
@@ -35,6 +35,7 @@
public static final String WAV = "/sdcard/media_api/music/rings_2ch.wav";
public static final String AMR = "/sdcard/media_api/music/test_amr_ietf.amr";
public static final String OGG = "/sdcard/media_api/music/Revelation.ogg";
+ public static final String SINE_200_1000 = "/sdcard/media_api/music/sine_200+1000Hz_44K_mo.wav";
public static final int MP3CBR_LENGTH = 71000;
public static final int MP3VBR_LENGTH = 71000;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioEffectTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioEffectTest.java
new file mode 100644
index 0000000..fd939ae
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioEffectTest.java
@@ -0,0 +1,1492 @@
+/*
+ * 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.mediaframeworktest.functional;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioEffect;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioTrack;
+import android.media.EnvironmentalReverb;
+import android.media.Equalizer;
+import android.media.MediaPlayer;
+
+import android.os.Looper;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+/**
+ * Junit / Instrumentation test case for the media AudioTrack api
+
+ */
+public class MediaAudioEffectTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+ private String TAG = "MediaAudioEffectTest";
+
+ private AudioEffect mEffect = null;
+ private boolean mHasControl = false;
+ private boolean mIsEnabled = false;
+ private int mParameterChanged = -1;
+ private MediaPlayer mMediaPlayer = null;
+ private boolean mInitialized = false;
+ private Looper mLooper = null;
+ private int mError = 0;
+ private final Object lock = new Object();
+
+ public MediaAudioEffectTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ private static void assumeTrue(String message, boolean cond) {
+ assertTrue("(assume)"+message, cond);
+ }
+
+ private void log(String testName, String message) {
+ Log.v(TAG, "["+testName+"] "+message);
+ }
+
+ private void loge(String testName, String message) {
+ Log.e(TAG, "["+testName+"] "+message);
+ }
+
+ //-----------------------------------------------------------------
+ // AUDIOEFFECT TESTS:
+ //----------------------------------
+
+ //-----------------------------------------------------------------
+ // 0 - static methods
+ //----------------------------------
+
+ //Test case 0.0: test queryEffects() and available effects
+ @LargeTest
+ public void test0_0QueryEffects() throws Exception {
+
+ AudioEffect.Descriptor[] desc = AudioEffect.queryEffects();
+
+ assertTrue("test0_0QueryEffects: number of effects < 4: "+desc.length, (desc.length >= 4));
+
+ boolean hasEQ = false;
+ boolean hasBassBoost = false;
+ boolean hasVirtualizer = false;
+ boolean hasEnvReverb = false;
+
+ for (int i = 0; i < desc.length; i++) {
+ if (desc[i].mType.equals(AudioEffect.EFFECT_TYPE_EQUALIZER)) {
+ hasEQ = true;
+ } if (desc[i].mType.equals(AudioEffect.EFFECT_TYPE_BASS_BOOST)) {
+ hasBassBoost = true;
+ } else if (desc[i].mType.equals(AudioEffect.EFFECT_TYPE_VIRTUALIZER)) {
+ hasVirtualizer = true;
+ }
+ else if (desc[i].mType.equals(AudioEffect.EFFECT_TYPE_ENV_REVERB)) {
+ hasEnvReverb = true;
+ }
+ }
+ assertTrue("test0_0QueryEffects: equalizer not found", hasEQ);
+ assertTrue("test0_0QueryEffects: bass boost not found", hasBassBoost);
+ assertTrue("test0_0QueryEffects: virtualizer not found", hasVirtualizer);
+ assertTrue("test0_0QueryEffects: environmental reverb not found", hasEnvReverb);
+ }
+
+ //-----------------------------------------------------------------
+ // 1 - constructor
+ //----------------------------------
+
+ //Test case 1.0: test constructor from effect type and get effect ID
+ @LargeTest
+ public void test1_0ConstructorFromType() throws Exception {
+ boolean result = true;
+ String msg = "test1_0ConstructorFromType()";
+ AudioEffect.Descriptor[] desc = AudioEffect.queryEffects();
+ assertTrue(msg+": no effects found", (desc.length != 0));
+ try {
+ AudioEffect effect = new AudioEffect(desc[0].mType,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ try {
+ assertTrue(msg +": invalid effect ID", (effect.getId() != 0));
+ } catch (IllegalStateException e) {
+ msg = msg.concat(": AudioEffect not initialized");
+ result = false;
+ } finally {
+ effect.release();
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Effect not found: "+desc[0].mName);
+ result = false;
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ result = false;
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.1: test constructor from effect uuid
+ @LargeTest
+ public void test1_1ConstructorFromUuid() throws Exception {
+ boolean result = true;
+ String msg = "test1_1ConstructorFromUuid()";
+ AudioEffect.Descriptor[] desc = AudioEffect.queryEffects();
+ assertTrue(msg+"no effects found", (desc.length != 0));
+ try {
+ AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_NULL,
+ desc[0].mUuid,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ effect.release();
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Effect not found: "+desc[0].mName);
+ result = false;
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ result = false;
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.2: test constructor failure from unknown type
+ @LargeTest
+ public void test1_2ConstructorUnknownType() throws Exception {
+ boolean result = false;
+ String msg = "test1_2ConstructorUnknownType()";
+
+ try {
+ AudioEffect effect = new AudioEffect(UUID.randomUUID(),
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ msg = msg.concat(": could create random AudioEffect");
+ if (effect != null) {
+ effect.release();
+ }
+ } catch (IllegalArgumentException e) {
+ result = true;
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.3: test getEnabled() failure when called on released effect
+ @LargeTest
+ public void test1_3GetEnabledAfterRelease() throws Exception {
+ boolean result = false;
+ String msg = "test1_3GetEnabledAfterRelease()";
+
+ try {
+ AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ effect.release();
+ try {
+ effect.getEnabled();
+ } catch (IllegalStateException e) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.4: test contructor on mediaPlayer audio session
+ @LargeTest
+ public void test1_4InsertOnMediaPlayer() throws Exception {
+ boolean result = false;
+ String msg = "test1_4InsertOnMediaPlayer()";
+
+ try {
+ MediaPlayer mp = new MediaPlayer();
+ mp.setDataSource(MediaNames.SHORTMP3);
+
+ AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ mp.getAudioSessionId());
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ try {
+ loge(msg, ": effect.setEnabled");
+ effect.setEnabled(true);
+ } catch (IllegalStateException e) {
+ msg = msg.concat(": AudioEffect not initialized");
+ }
+
+ result = true;
+ effect.release();
+ mp.release();
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ } catch (Exception e){
+ loge(msg, "Could not create media player:" + e);
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.5: test auxiliary effect attachement on MediaPlayer
+ @LargeTest
+ public void test1_5AuxiliaryOnMediaPlayer() throws Exception {
+ boolean result = false;
+ String msg = "test1_5AuxiliaryOnMediaPlayer()";
+
+ try {
+ MediaPlayer mp = new MediaPlayer();
+ mp.setDataSource(MediaNames.SHORTMP3);
+
+ AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ mp.attachAuxEffect(effect.getId());
+ mp.setAuxEffectSendLevel(1.0f);
+ result = true;
+ effect.release();
+ mp.release();
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ } catch (Exception e){
+ loge(msg, "Could not create media player:" + e);
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.6: test auxiliary effect attachement failure before setDatasource
+ @LargeTest
+ public void test1_6AuxiliaryOnMediaPlayerFailure() throws Exception {
+ boolean result = false;
+ String msg = "test1_6AuxiliaryOnMediaPlayerFailure()";
+
+ try {
+ createMediaPlayerLooper();
+ synchronized(lock) {
+ try {
+ lock.wait(1000);
+ } catch(Exception e) {
+ Log.e(TAG, "Looper creation: wait was interrupted.");
+ }
+ }
+ assertTrue(mInitialized); // mMediaPlayer has been initialized?
+ mError = 0;
+
+ AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ synchronized(lock) {
+ try {
+ mMediaPlayer.attachAuxEffect(effect.getId());
+ lock.wait(1000);
+ } catch(Exception e) {
+ Log.e(TAG, "Attach effect: wait was interrupted.");
+ }
+ }
+ assertTrue(msg + ": no error on attachAuxEffect", mError != 0);
+ result = true;
+ effect.release();
+ terminateMediaPlayerLooper();
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ } catch (Exception e){
+ loge(msg, "Could not create media player:" + e);
+ }
+ assertTrue(msg, result);
+ }
+
+
+ //Test case 1.7: test auxiliary effect attachement on AudioTrack
+ @LargeTest
+ public void test1_7AuxiliaryOnAudioTrack() throws Exception {
+ boolean result = false;
+ String msg = "test1_7AuxiliaryOnAudioTrack()";
+
+ try {
+ AudioTrack track = new AudioTrack(
+ AudioManager.STREAM_MUSIC,
+ 44100,
+ AudioFormat.CHANNEL_OUT_MONO,
+ AudioFormat.ENCODING_PCM_16BIT,
+ AudioTrack.getMinBufferSize(44100,
+ AudioFormat.CHANNEL_OUT_MONO,
+ AudioFormat.ENCODING_PCM_16BIT),
+ AudioTrack.MODE_STREAM);
+ assertNotNull(msg + ": could not create AudioTrack", track);
+ AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+
+ track.attachAuxEffect(effect.getId());
+ track.setAuxEffectSendLevel(1.0f);
+ result = true;
+ effect.release();
+ track.release();
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 2 - enable/ disable
+ //----------------------------------
+
+
+ //Test case 2.0: test setEnabled() and getEnabled() in valid state
+ @LargeTest
+ public void test2_0SetEnabledGetEnabled() throws Exception {
+ boolean result = false;
+ String msg = "test2_0SetEnabledGetEnabled()";
+
+ try {
+ AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ try {
+ effect.setEnabled(true);
+ assertTrue(msg + ": invalid state from getEnabled", effect.getEnabled());
+ effect.setEnabled(false);
+ assertFalse(msg + ": invalid state to getEnabled", effect.getEnabled());
+ result = true;
+ } catch (IllegalStateException e) {
+ msg = msg.concat(": setEnabled() in wrong state");
+ } finally {
+ effect.release();
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 2.1: test setEnabled() throws exception after release
+ @LargeTest
+ public void test2_1SetEnabledAfterRelease() throws Exception {
+ boolean result = false;
+ String msg = "test2_1SetEnabledAfterRelease()";
+
+ try {
+ AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ effect.release();
+ try {
+ effect.setEnabled(true);
+ } catch (IllegalStateException e) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 3 - set parameters
+ //----------------------------------
+
+ //Test case 3.0: test setParameter(byte[], byte[])
+ @LargeTest
+ public void test3_0SetParameterByteArrayByteArray() throws Exception {
+ boolean result = false;
+ String msg = "test3_0SetParameterByteArrayByteArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ byte[] param = intToByteArray(Equalizer.PARAM_CURRENT_PRESET);
+ byte[] value = shortToByteArray((short)0);
+ if (effect.setParameter(param, value) == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": setParameter() rejected");
+ loge(msg, "setParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("setParameter() called in wrong state");
+ loge(msg, "setParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 3.1: test setParameter(int, int)
+ @LargeTest
+ public void test3_1SetParameterIntInt() throws Exception {
+ boolean result = false;
+ String msg = "test3_1SetParameterIntInt()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ if (effect.setParameter(EnvironmentalReverb.PARAM_DECAY_TIME, 0)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": setParameter() rejected");
+ loge(msg, "setParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("setParameter() called in wrong state");
+ loge(msg, "setParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 3.2: test setParameter(int, short)
+ @LargeTest
+ public void test3_2SetParameterIntShort() throws Exception {
+ boolean result = false;
+ String msg = "test3_2SetParameterIntShort()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ if (effect.setParameter(Equalizer.PARAM_CURRENT_PRESET, (short)0)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": setParameter() rejected");
+ loge(msg, "setParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("setParameter() called in wrong state");
+ loge(msg, "setParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 3.3: test setParameter(int, byte[])
+ @LargeTest
+ public void test3_3SetParameterIntByteArray() throws Exception {
+ boolean result = false;
+ String msg = "test3_3SetParameterIntByteArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ byte[] value = shortToByteArray((short)0);
+ if (effect.setParameter(Equalizer.PARAM_CURRENT_PRESET, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": setParameter() rejected");
+ loge(msg, "setParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("setParameter() called in wrong state");
+ loge(msg, "setParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 3.4: test setParameter(int[], int[])
+ @LargeTest
+ public void test3_4SetParameterIntArrayIntArray() throws Exception {
+ boolean result = false;
+ String msg = "test3_4SetParameterIntArrayIntArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ int[] param = new int[1];
+ int[] value = new int[1];
+ param[0] = EnvironmentalReverb.PARAM_DECAY_TIME;
+ value[0] = 0;
+ if (effect.setParameter(param, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": setParameter() rejected");
+ loge(msg, "setParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("setParameter() called in wrong state");
+ loge(msg, "setParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 3.5: test setParameter(int[], short[])
+ @LargeTest
+ public void test3_5SetParameterIntArrayShortArray() throws Exception {
+ boolean result = false;
+ String msg = "test3_5SetParameterIntArrayShortArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ int[] param = new int[1];
+ short[] value = new short[1];
+ param[0] = Equalizer.PARAM_CURRENT_PRESET;
+ value[0] = (short)0;
+ if (effect.setParameter(param, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": setParameter() rejected");
+ loge(msg, "setParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("setParameter() called in wrong state");
+ loge(msg, "setParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 3.6: test setParameter(int[], byte[])
+ @LargeTest
+ public void test3_6SetParameterIntArrayByteArray() throws Exception {
+ boolean result = false;
+ String msg = "test3_6SetParameterIntArrayByteArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ int[] param = new int[1];
+ byte[] value = shortToByteArray((short)0);
+ param[0] = Equalizer.PARAM_CURRENT_PRESET;
+ if (effect.setParameter(param, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": setParameter() rejected");
+ loge(msg, "setParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("setParameter() called in wrong state");
+ loge(msg, "setParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 3.7: test setParameter() throws exception after release()
+ @LargeTest
+ public void test3_7SetParameterAfterRelease() throws Exception {
+ boolean result = false;
+ String msg = "test3_7SetParameterAfterRelease()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ effect.release();
+ effect.setParameter(Equalizer.PARAM_CURRENT_PRESET, (short)0);
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": setParameter() rejected");
+ loge(msg, "setParameter() rejected");
+ } catch (IllegalStateException e) {
+ result = true;
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 4 - get parameters
+ //----------------------------------
+
+ //Test case 4.0: test getParameter(byte[], byte[])
+ @LargeTest
+ public void test4_0GetParameterByteArrayByteArray() throws Exception {
+ boolean result = false;
+ String msg = "test4_0GetParameterByteArrayByteArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ byte[] param = intToByteArray(Equalizer.PARAM_CURRENT_PRESET);
+ byte[] value = new byte[2];
+ if (effect.getParameter(param, value) == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": getParameter() rejected");
+ loge(msg, "getParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("getParameter() called in wrong state");
+ loge(msg, "getParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 4.1: test getParameter(int, int[])
+ @LargeTest
+ public void test4_1GetParameterIntIntArray() throws Exception {
+ boolean result = false;
+ String msg = "test4_1GetParameterIntIntArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ int[] value = new int[1];
+ if (effect.getParameter(EnvironmentalReverb.PARAM_DECAY_TIME, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": getParameter() rejected");
+ loge(msg, "getParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("getParameter() called in wrong state");
+ loge(msg, "getParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 4.2: test getParameter(int, short[])
+ @LargeTest
+ public void test4_2GetParameterIntShortArray() throws Exception {
+ boolean result = false;
+ String msg = "test4_2GetParameterIntShortArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ short[] value = new short[1];
+ if (effect.getParameter(Equalizer.PARAM_CURRENT_PRESET, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": getParameter() rejected");
+ loge(msg, "getParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("getParameter() called in wrong state");
+ loge(msg, "getParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 4.3: test getParameter(int, byte[])
+ @LargeTest
+ public void test4_3GetParameterIntByteArray() throws Exception {
+ boolean result = false;
+ String msg = "test4_3GetParameterIntByteArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ byte[] value = new byte[2];
+ if (effect.getParameter(Equalizer.PARAM_CURRENT_PRESET, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": getParameter() rejected");
+ loge(msg, "getParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("getParameter() called in wrong state");
+ loge(msg, "getParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 4.4: test getParameter(int[], int[])
+ @LargeTest
+ public void test4_4GetParameterIntArrayIntArray() throws Exception {
+ boolean result = false;
+ String msg = "test4_4GetParameterIntArrayIntArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ int[] param = new int[1];
+ int[] value = new int[1];
+ param[0] = EnvironmentalReverb.PARAM_DECAY_TIME;
+ if (effect.getParameter(param, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": getParameter() rejected");
+ loge(msg, "getParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("getParameter() called in wrong state");
+ loge(msg, "getParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 4.5: test getParameter(int[], short[])
+ @LargeTest
+ public void test4_5GetParameterIntArrayShortArray() throws Exception {
+ boolean result = false;
+ String msg = "test4_5GetParameterIntArrayShortArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ int[] param = new int[1];
+ short[] value = new short[1];
+ param[0] = Equalizer.PARAM_CURRENT_PRESET;
+ if (effect.getParameter(param, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": getParameter() rejected");
+ loge(msg, "getParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("getParameter() called in wrong state");
+ loge(msg, "getParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 4.6: test getParameter(int[], byte[])
+ @LargeTest
+ public void test4_6GetParameterIntArrayByteArray() throws Exception {
+ boolean result = false;
+ String msg = "test4_6GetParameterIntArrayByteArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ int[] param = new int[1];
+ byte[] value = new byte[2];
+ param[0] = Equalizer.PARAM_CURRENT_PRESET;
+ if (effect.getParameter(param, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": getParameter() rejected");
+ loge(msg, "getParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("getParameter() called in wrong state");
+ loge(msg, "getParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 4.7: test getParameter() throws exception after release()
+ @LargeTest
+ public void test4_7GetParameterAfterRelease() throws Exception {
+ boolean result = false;
+ String msg = "test4_7GetParameterAfterRelease()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ effect.release();
+ short[] value = new short[1];
+ effect.getParameter(Equalizer.PARAM_CURRENT_PRESET, value);
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": getParameter() rejected");
+ loge(msg, "getParameter() rejected");
+ } catch (IllegalStateException e) {
+ result = true;
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 5 priority and listeners
+ //----------------------------------
+
+ //Test case 5.0: test control passed to higher priority client
+ @LargeTest
+ public void test5_0setEnabledLowerPriority() throws Exception {
+ boolean result = false;
+ String msg = "test5_0setEnabledLowerPriority()";
+ AudioEffect effect1 = null;
+ AudioEffect effect2 = null;
+ try {
+ effect1 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ effect2 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 1,
+ 0);
+
+ assertNotNull(msg + ": could not create AudioEffect", effect1);
+ assertNotNull(msg + ": could not create AudioEffect", effect2);
+
+ assertTrue(msg + ": Effect2 does not have control", effect2.hasControl());
+ assertFalse(msg + ": Effect1 has control", effect1.hasControl());
+ assertTrue(msg + ": Effect1 can enable",
+ effect1.setEnabled(true) == AudioEffect.ERROR_INVALID_OPERATION);
+ assertFalse(msg + ": Effect1 has enabled", effect2.getEnabled());
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Effect not found");
+ result = false;
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ result = false;
+ } finally {
+ if (effect1 != null) {
+ effect1.release();
+ }
+ if (effect2 != null) {
+ effect2.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 5.1: test control passed to higher priority client
+ @LargeTest
+ public void test5_1setParameterLowerPriority() throws Exception {
+ boolean result = false;
+ String msg = "test5_1setParameterLowerPriority()";
+ AudioEffect effect1 = null;
+ AudioEffect effect2 = null;
+ try {
+ effect1 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ effect2 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 1,
+ 0);
+
+ assertNotNull(msg + ": could not create AudioEffect", effect1);
+ assertNotNull(msg + ": could not create AudioEffect", effect2);
+
+ int status = effect2.setParameter(Equalizer.PARAM_CURRENT_PRESET, (short)0);
+ assertEquals(msg + ": Effect2 setParameter failed",
+ AudioEffect.SUCCESS, status);
+
+ status = effect1.setParameter(Equalizer.PARAM_CURRENT_PRESET, (short)1);
+ assertEquals(msg + ": Effect1 setParameter did not fail",
+ AudioEffect.ERROR_INVALID_OPERATION, status);
+
+ short[] value = new short[1];
+ status = effect2.getParameter(Equalizer.PARAM_CURRENT_PRESET, value);
+ assertEquals(msg + ": Effect2 getParameter failed",
+ AudioEffect.SUCCESS, status);
+ assertEquals(msg + ": Effect1 changed parameter",
+ (short)0, value[0]);
+
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Effect not found");
+ result = false;
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ result = false;
+ } finally {
+ if (effect1 != null) {
+ effect1.release();
+ }
+ if (effect2 != null) {
+ effect2.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 5.2: test control status listener
+ @LargeTest
+ public void test5_2ControlStatusListener() throws Exception {
+ boolean result = false;
+ String msg = "test5_2ControlStatusListener()";
+ mEffect = null;
+ AudioEffect effect2 = null;
+ try {
+ mHasControl = true;
+ createListenerLooper(true, false, false);
+ synchronized(lock) {
+ try {
+ lock.wait(1000);
+ } catch(Exception e) {
+ Log.e(TAG, "Looper creation: wait was interrupted.");
+ }
+ }
+ assertTrue(mInitialized);
+ synchronized(lock) {
+ try {
+ effect2 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 1,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect2);
+ lock.wait(1000);
+ } catch(Exception e) {
+ Log.e(TAG, "Create second effect: wait was interrupted.");
+ }
+ }
+ assertFalse(msg + ": effect control not lost by effect1", mHasControl);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ } catch (Exception e){
+ loge(msg, "Could not create media player:" + e);
+ } finally {
+ terminateListenerLooper();
+ if (effect2 != null) {
+ effect2.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 5.3: test enable status listener
+ @LargeTest
+ public void test5_3EnableStatusListener() throws Exception {
+ boolean result = false;
+ String msg = "test5_3EnableStatusListener()";
+ mEffect = null;
+ AudioEffect effect2 = null;
+ try {
+ createListenerLooper(false, true, false);
+ synchronized(lock) {
+ try {
+ lock.wait(1000);
+ } catch(Exception e) {
+ Log.e(TAG, "Looper creation: wait was interrupted.");
+ }
+ }
+ assertTrue(mInitialized);
+ mEffect.setEnabled(true);
+ mIsEnabled = true;
+ effect2 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 1,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect2);
+ assertTrue(msg + ": effect not enabled", effect2.getEnabled());
+ synchronized(lock) {
+ try {
+ effect2.setEnabled(false);
+ lock.wait(1000);
+ } catch(Exception e) {
+ Log.e(TAG, "Create second effect: wait was interrupted.");
+ }
+ }
+ assertFalse(msg + ": enable status not updated", mIsEnabled);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ } catch (Exception e){
+ loge(msg, "Could not create media player:" + e);
+ } finally {
+ terminateListenerLooper();
+ if (effect2 != null) {
+ effect2.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 5.4: test parameter changed listener
+ @LargeTest
+ public void test5_4ParameterChangedListener() throws Exception {
+ boolean result = false;
+ String msg = "test5_4ParameterChangedListener()";
+ mEffect = null;
+ AudioEffect effect2 = null;
+ try {
+ createListenerLooper(false, false, true);
+ synchronized(lock) {
+ try {
+ lock.wait(1000);
+ } catch(Exception e) {
+ Log.e(TAG, "Looper creation: wait was interrupted.");
+ }
+ }
+ assertTrue(mInitialized);
+ effect2 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 1,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect2);
+ synchronized(lock) {
+ try {
+ mParameterChanged = -1;
+ effect2.setParameter(Equalizer.PARAM_CURRENT_PRESET, (short)0);
+ lock.wait(1000);
+ } catch(Exception e) {
+ Log.e(TAG, "Create second effect: wait was interrupted.");
+ }
+ }
+ assertEquals(msg + ": parameter change not received",
+ Equalizer.PARAM_CURRENT_PRESET, mParameterChanged);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ } catch (Exception e){
+ loge(msg, "Could not create media player:" + e);
+ } finally {
+ terminateListenerLooper();
+ if (effect2 != null) {
+ effect2.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 6 command method
+ //----------------------------------
+
+
+ //Test case 6.0: test command method
+ @LargeTest
+ public void test6_0Command() throws Exception {
+ boolean result = false;
+ String msg = "test6_0Command()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ try {
+ byte[] cmd = new byte[0];
+ byte[] reply = new byte[4];
+ int status = effect.command(3, cmd, reply);
+ assertEquals(msg + ": command failed", AudioEffect.SUCCESS, status);
+ assertTrue(msg + ": effect not enabled", effect.getEnabled());
+ result = true;
+ } catch (IllegalStateException e) {
+ msg = msg.concat(": command in illegal state");
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ } catch (Exception e){
+ loge(msg, "Could not create media player:" + e);
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // private methods
+ //----------------------------------
+
+ /*
+ * Initializes the message looper so that the MediaPlayer object can
+ * receive the callback messages.
+ */
+ private void createMediaPlayerLooper() {
+ new Thread() {
+ @Override
+ public void run() {
+ // Set up a looper to be used by mMediaPlayer.
+ Looper.prepare();
+
+ // Save the looper so that we can terminate this thread
+ // after we are done with it.
+ mLooper = Looper.myLooper();
+
+ mMediaPlayer = new MediaPlayer();
+ mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+ public boolean onError(MediaPlayer player, int what, int extra) {
+ synchronized(lock) {
+ mError = what;
+ lock.notify();
+ }
+ return true;
+ }
+ });
+ mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+ public void onCompletion(MediaPlayer player) {
+ synchronized(lock) {
+ lock.notify();
+ }
+ }
+ });
+ synchronized(lock) {
+ mInitialized = true;
+ lock.notify();
+ }
+ Looper.loop(); // Blocks forever until Looper.quit() is called.
+ }
+ }.start();
+ }
+ /*
+ * Terminates the message looper thread.
+ */
+ private void terminateMediaPlayerLooper() {
+ if (mLooper != null) {
+ mLooper.quit();
+ mLooper = null;
+ }
+ if (mMediaPlayer != null) {
+ mMediaPlayer.release();
+ }
+ }
+
+ /*
+ * Initializes the message looper fro effect listener
+ */
+ class ListenerThread extends Thread {
+ boolean mControl;
+ boolean mEnable;
+ boolean mParameter;
+
+ public ListenerThread(boolean control, boolean enable, boolean parameter) {
+ super();
+ mControl = control;
+ mEnable = enable;
+ mParameter = parameter;
+ }
+ }
+ private void createListenerLooper(boolean control, boolean enable, boolean parameter) {
+
+ new ListenerThread(control, enable, parameter) {
+ @Override
+ public void run() {
+ // Set up a looper to be used by mEffect.
+ Looper.prepare();
+
+ // Save the looper so that we can terminate this thread
+ // after we are done with it.
+ mLooper = Looper.myLooper();
+
+ mEffect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull("could not create AudioEffect", mEffect);
+
+ if (mControl) {
+ mEffect.setControlStatusListener(new AudioEffect.OnControlStatusChangeListener() {
+ public void onControlStatusChange(AudioEffect effect, boolean controlGranted) {
+ synchronized(lock) {
+ if (effect == mEffect) {
+ mHasControl = controlGranted;
+ lock.notify();
+ }
+ }
+ }
+ });
+ }
+ if (mEnable) {
+ mEffect.setEnableStatusListener(new AudioEffect.OnEnableStatusChangeListener() {
+ public void onEnableStatusChange(AudioEffect effect, boolean enabled) {
+ synchronized(lock) {
+ if (effect == mEffect) {
+ mIsEnabled = enabled;
+ lock.notify();
+ }
+ }
+ }
+ });
+ }
+ if (mParameter) {
+ mEffect.setParameterListener(new AudioEffect.OnParameterChangeListener() {
+ public void onParameterChange(AudioEffect effect, int status, byte[] param,
+ byte[] value) {
+ synchronized(lock) {
+ if (effect == mEffect) {
+ mParameterChanged = byteArrayToInt(param);
+ lock.notify();
+ }
+ }
+ }
+ });
+ }
+
+ synchronized(lock) {
+ mInitialized = true;
+ lock.notify();
+ }
+ Looper.loop(); // Blocks forever until Looper.quit() is called.
+ }
+ }.start();
+ }
+ /*
+ * Terminates the listener looper thread.
+ */
+ private void terminateListenerLooper() {
+ if (mEffect != null) {
+ mEffect.release();
+ mEffect = null;
+ }
+ if (mLooper != null) {
+ mLooper.quit();
+ mLooper = null;
+ }
+ }
+
+ protected int byteArrayToInt(byte[] valueBuf) {
+ return byteArrayToInt(valueBuf, 0);
+
+ }
+
+ protected int byteArrayToInt(byte[] valueBuf, int offset) {
+ ByteBuffer converter = ByteBuffer.wrap(valueBuf);
+ converter.order(ByteOrder.nativeOrder());
+ return converter.getInt(offset);
+
+ }
+
+ protected byte[] intToByteArray(int value) {
+ ByteBuffer converter = ByteBuffer.allocate(4);
+ converter.order(ByteOrder.nativeOrder());
+ converter.putInt(value);
+ return converter.array();
+ }
+
+ protected short byteArrayToShort(byte[] valueBuf) {
+ return byteArrayToShort(valueBuf, 0);
+ }
+
+ protected short byteArrayToShort(byte[] valueBuf, int offset) {
+ ByteBuffer converter = ByteBuffer.wrap(valueBuf);
+ converter.order(ByteOrder.nativeOrder());
+ return converter.getShort(offset);
+
+ }
+
+ protected byte[] shortToByteArray(short value) {
+ ByteBuffer converter = ByteBuffer.allocate(2);
+ converter.order(ByteOrder.nativeOrder());
+ short sValue = (short) value;
+ converter.putShort(sValue);
+ return converter.array();
+ }
+
+}
+
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java
new file mode 100644
index 0000000..8a68c5e
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java
@@ -0,0 +1,334 @@
+/*
+ * 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.mediaframeworktest.functional;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioEffect;
+import android.media.AudioManager;
+import android.media.BassBoost;
+import android.media.Visualizer;
+import android.media.MediaPlayer;
+
+import android.os.Looper;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+/**
+ * Junit / Instrumentation test case for the media AudioTrack api
+
+ */
+public class MediaBassBoostTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+ private String TAG = "MediaBassBoostTest";
+ private final static int MIN_ENERGY_RATIO_2 = 4;
+ private final static short TEST_STRENGTH = 500;
+
+ private BassBoost mBassBoost = null;
+ private int mSession = -1;
+
+ public MediaBassBoostTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ releaseBassBoost();
+ }
+
+ private static void assumeTrue(String message, boolean cond) {
+ assertTrue("(assume)"+message, cond);
+ }
+
+ private void log(String testName, String message) {
+ Log.v(TAG, "["+testName+"] "+message);
+ }
+
+ private void loge(String testName, String message) {
+ Log.e(TAG, "["+testName+"] "+message);
+ }
+
+ //-----------------------------------------------------------------
+ // BASS BOOST TESTS:
+ //----------------------------------
+
+
+ //-----------------------------------------------------------------
+ // 0 - constructor
+ //----------------------------------
+
+ //Test case 0.0: test constructor and release
+ @LargeTest
+ public void test0_0ConstructorAndRelease() throws Exception {
+ boolean result = false;
+ String msg = "test1_0ConstructorAndRelease()";
+ BassBoost bb = null;
+ try {
+ bb = new BassBoost(0, 0);
+ assertNotNull(msg + ": could not create BassBoost", bb);
+ try {
+ assertTrue(msg +": invalid effect ID", (bb.getId() != 0));
+ } catch (IllegalStateException e) {
+ msg = msg.concat(": BassBoost not initialized");
+ }
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": BassBoost not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ } finally {
+ if (bb != null) {
+ bb.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 1 - get/set parameters
+ //----------------------------------
+
+ //Test case 1.0: test strength
+ @LargeTest
+ public void test1_0Strength() throws Exception {
+ boolean result = false;
+ String msg = "test1_0Strength()";
+ getBassBoost(0);
+ try {
+ if (mBassBoost.getStrengthSupported()) {
+ mBassBoost.setStrength((short)TEST_STRENGTH);
+ short strength = mBassBoost.getRoundedStrength();
+ // allow 10% difference between set strength and rounded strength
+ assertTrue(msg +": got incorrect strength",
+ ((float)strength > (float)TEST_STRENGTH * 0.9f) &&
+ ((float)strength < (float)TEST_STRENGTH * 1.1f));
+ } else {
+ short strength = mBassBoost.getRoundedStrength();
+ assertTrue(msg +": got incorrect strength", strength >= 0 && strength <= 1000);
+ }
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseBassBoost();
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.1: test properties
+ @LargeTest
+ public void test1_1Properties() throws Exception {
+ boolean result = false;
+ String msg = "test1_1Properties()";
+ getBassBoost(0);
+ try {
+ BassBoost.Settings settings = mBassBoost.getProperties();
+ String str = settings.toString();
+ settings = new BassBoost.Settings(str);
+ mBassBoost.setProperties(settings);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseBassBoost();
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 2 - Effect action
+ //----------------------------------
+
+ //Test case 2.0: test actual bass boost influence on sound
+ @LargeTest
+ public void test2_0SoundModification() throws Exception {
+ boolean result = false;
+ String msg = "test2_0SoundModification()";
+ EnergyProbe probe = null;
+ AudioEffect vc = null;
+ MediaPlayer mp = null;
+ AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+ int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ am.setStreamVolume(AudioManager.STREAM_MUSIC,
+ am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+ 0);
+
+ try {
+ probe = new EnergyProbe(0);
+ // creating a volume controller on output mix ensures that ro.audio.silent mutes
+ // audio after the effects and not before
+ vc = new AudioEffect(
+ AudioEffect.EFFECT_TYPE_NULL,
+ UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
+ 0,
+ 0);
+ vc.setEnabled(true);
+
+ mp = new MediaPlayer();
+ mp.setDataSource(MediaNames.SINE_200_1000);
+ mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ getBassBoost(mp.getAudioSessionId());
+ mp.prepare();
+ mp.start();
+ Thread.sleep(200);
+ // measure reference energy around 1kHz
+ int refEnergy200 = probe.capture(200);
+ int refEnergy1000 = probe.capture(1000);
+ mBassBoost.setStrength((short)1000);
+ mBassBoost.setEnabled(true);
+ Thread.sleep(500);
+ // measure energy around 1kHz with band level at min
+ int energy200 = probe.capture(200);
+ int energy1000 = probe.capture(1000);
+ // verify that the energy ration between low and high frequencies is at least
+ // MIN_ENERGY_RATIO_2 times higher with bassboost on.
+ assertTrue(msg + ": bass boost has no effect",
+ ((float)energy200/(float)energy1000) >
+ (MIN_ENERGY_RATIO_2 * ((float)refEnergy200/(float)refEnergy1000)));
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } catch (InterruptedException e) {
+ loge(msg, "sleep() interrupted");
+ }
+ finally {
+ releaseBassBoost();
+ if (mp != null) {
+ mp.release();
+ }
+ if (vc != null) {
+ vc.release();
+ }
+ if (probe != null) {
+ probe.release();
+ }
+ am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+ }
+ assertTrue(msg, result);
+ }
+ //-----------------------------------------------------------------
+ // private methods
+ //----------------------------------
+
+ private class EnergyProbe {
+ Visualizer mVisualizer = null;
+ private byte[] mFft = new byte[1024];
+
+ public EnergyProbe(int session) {
+ mVisualizer = new Visualizer(session);
+ mVisualizer.setCaptureSize(1024);
+ }
+
+ public int capture(int freq) throws InterruptedException {
+ int energy = 0;
+ int count = 0;
+ if (mVisualizer != null) {
+ mVisualizer.setEnabled(true);
+ for (int i = 0; i < 10; i++) {
+ if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) {
+ // TODO: check speex FFT as it seems to return only the number of points
+ // correspondong to valid part of the spectrum (< Fs).
+ // e.g., if the number of points is 1024, it covers the frequency range
+ // 0 to 22050 instead of 0 to 44100 as expected from an FFT.
+ int bin = freq / (22050 / 1024);
+ int tmp = 0;
+ for (int j = bin-2; j < bin+3; j++) {
+ tmp += (int)mFft[j] * (int)mFft[j];
+ }
+ energy += tmp/5;
+ count++;
+ }
+ Thread.sleep(50);
+ }
+ mVisualizer.setEnabled(false);
+ }
+ if (count == 0) {
+ return 0;
+ }
+ return energy/count;
+ }
+
+ public void release() {
+ if (mVisualizer != null) {
+ mVisualizer.release();
+ mVisualizer = null;
+ }
+ }
+ }
+
+ private void getBassBoost(int session) {
+ if (mBassBoost == null || session != mSession) {
+ if (session != mSession && mBassBoost != null) {
+ mBassBoost.release();
+ mBassBoost = null;
+ }
+ try {
+ mBassBoost = new BassBoost(0, session);
+ mSession = session;
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "getBassBoost() BassBoost not found exception: "+e);
+ } catch (UnsupportedOperationException e) {
+ Log.e(TAG, "getBassBoost() Effect library not loaded exception: "+e);
+ }
+ }
+ assertNotNull("could not create mBassBoost", mBassBoost);
+ }
+
+ private void releaseBassBoost() {
+ if (mBassBoost != null) {
+ mBassBoost.release();
+ mBassBoost = null;
+ }
+ }
+
+}
+
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java
new file mode 100644
index 0000000..e46887b
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java
@@ -0,0 +1,397 @@
+/*
+ * 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.mediaframeworktest.functional;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioEffect;
+import android.media.AudioManager;
+import android.media.Equalizer;
+import android.media.Visualizer;
+import android.media.MediaPlayer;
+
+import android.os.Looper;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+/**
+ * Junit / Instrumentation test case for the media AudioTrack api
+
+ */
+public class MediaEqualizerTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+ private String TAG = "MediaEqualizerTest";
+ private final static int MIN_NUMBER_OF_BANDS = 4;
+ private final static int MIN_BAND_LEVEL = -1500;
+ private final static int MAX_BAND_LEVEL = 1500;
+ private final static int TEST_FREQUENCY_MILLIHERTZ = 1000000;
+ private final static int MIN_NUMBER_OF_PRESETS = 4;
+ private Equalizer mEqualizer = null;
+ private int mSession = -1;
+
+ public MediaEqualizerTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ releaseEqualizer();
+ }
+
+ private static void assumeTrue(String message, boolean cond) {
+ assertTrue("(assume)"+message, cond);
+ }
+
+ private void log(String testName, String message) {
+ Log.v(TAG, "["+testName+"] "+message);
+ }
+
+ private void loge(String testName, String message) {
+ Log.e(TAG, "["+testName+"] "+message);
+ }
+
+ //-----------------------------------------------------------------
+ // EQUALIZER TESTS:
+ //----------------------------------
+
+
+ //-----------------------------------------------------------------
+ // 0 - constructor
+ //----------------------------------
+
+ //Test case 0.0: test constructor and release
+ @LargeTest
+ public void test0_0ConstructorAndRelease() throws Exception {
+ boolean result = false;
+ String msg = "test1_0ConstructorAndRelease()";
+ Equalizer eq = null;
+ try {
+ eq = new Equalizer(0, 0);
+ assertNotNull(msg + ": could not create Equalizer", eq);
+ try {
+ assertTrue(msg +": invalid effect ID", (eq.getId() != 0));
+ } catch (IllegalStateException e) {
+ msg = msg.concat(": Equalizer not initialized");
+ }
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ } finally {
+ if (eq != null) {
+ eq.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+
+ //-----------------------------------------------------------------
+ // 1 - get/set parameters
+ //----------------------------------
+
+ //Test case 1.0: test setBandLevel() and getBandLevel()
+ @LargeTest
+ public void test1_0BandLevel() throws Exception {
+ boolean result = false;
+ String msg = "test1_0BandLevel()";
+ getEqualizer(0);
+ try {
+ short numBands = mEqualizer.getNumberOfBands();
+ assertTrue(msg + ": not enough bands", numBands >= MIN_NUMBER_OF_BANDS);
+
+ short[] levelRange = mEqualizer.getBandLevelRange();
+ assertTrue(msg + ": min level too high", levelRange[0] <= MIN_BAND_LEVEL);
+ assertTrue(msg + ": max level too low", levelRange[1] >= MAX_BAND_LEVEL);
+
+ mEqualizer.setBandLevel((short)0, levelRange[1]);
+ short level = mEqualizer.getBandLevel((short)0);
+ // 10% margin on actual level compared to requested level
+ assertTrue(msg + ": setBandLevel failed",
+ ((float)level > (float)levelRange[1] * 0.9f) &&
+ ((float)level < (float)levelRange[1] * 1.1f));
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseEqualizer();
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.1: test band frequency
+ @LargeTest
+ public void test1_1BandFrequency() throws Exception {
+ boolean result = false;
+ String msg = "test1_1BandFrequency()";
+ getEqualizer(0);
+ try {
+ short band = mEqualizer.getBand(TEST_FREQUENCY_MILLIHERTZ);
+ assertTrue(msg + ": getBand failed", band >= 0);
+ int[] freqRange = mEqualizer.getBandFreqRange(band);
+ assertTrue(msg + ": getBandFreqRange failed",
+ (freqRange[0] <= TEST_FREQUENCY_MILLIHERTZ) &&
+ (freqRange[1] >= TEST_FREQUENCY_MILLIHERTZ));
+ int freq = mEqualizer.getCenterFreq(band);
+ assertTrue(msg + ": getCenterFreq failed",
+ (freqRange[0] <= freq) && (freqRange[1] >= freq));
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseEqualizer();
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.2: test presets
+ @LargeTest
+ public void test1_2Presets() throws Exception {
+ boolean result = false;
+ String msg = "test1_2Presets()";
+ getEqualizer(0);
+ try {
+ short numPresets = mEqualizer.getNumberOfPresets();
+ assertTrue(msg + ": getNumberOfPresets failed", numPresets >= MIN_NUMBER_OF_PRESETS);
+ mEqualizer.usePreset((short)(numPresets - 1));
+ short preset = mEqualizer.getCurrentPreset();
+ assertEquals(msg + ": usePreset failed", preset, (short)(numPresets - 1));
+ String name = mEqualizer.getPresetName(preset);
+ assertNotNull(msg + ": getPresetName failed", name);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseEqualizer();
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.3: test properties
+ @LargeTest
+ public void test1_3Properties() throws Exception {
+ boolean result = false;
+ String msg = "test1_3Properties()";
+ getEqualizer(0);
+ try {
+ Equalizer.Settings settings = mEqualizer.getProperties();
+ String str = settings.toString();
+ settings = new Equalizer.Settings(str);
+ mEqualizer.setProperties(settings);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseEqualizer();
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 2 - Effect action
+ //----------------------------------
+
+ //Test case 2.0: test that the equalizer actually alters the sound
+ @LargeTest
+ public void test2_0SoundModification() throws Exception {
+ boolean result = false;
+ String msg = "test2_0SoundModification()";
+ EnergyProbe probe = null;
+ AudioEffect vc = null;
+ MediaPlayer mp = null;
+ AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+ int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ am.setStreamVolume(AudioManager.STREAM_MUSIC,
+ am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+ 0);
+ try {
+ probe = new EnergyProbe(0);
+ // creating a volume controller on output mix ensures that ro.audio.silent mutes
+ // audio after the effects and not before
+ vc = new AudioEffect(
+ AudioEffect.EFFECT_TYPE_NULL,
+ UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
+ 0,
+ 0);
+ vc.setEnabled(true);
+
+ mp = new MediaPlayer();
+ mp.setDataSource(MediaNames.SINE_200_1000);
+ mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ getEqualizer(mp.getAudioSessionId());
+ mp.prepare();
+ mp.start();
+ Thread.sleep(500);
+ // measure reference energy around 1kHz
+ int refEnergy = probe.capture(1000);
+ short band = mEqualizer.getBand(1000000);
+ short[] levelRange = mEqualizer.getBandLevelRange();
+ mEqualizer.setBandLevel(band, levelRange[0]);
+ mEqualizer.setEnabled(true);
+ Thread.sleep(500);
+ // measure energy around 1kHz with band level at min
+ int energy = probe.capture(1000);
+ assertTrue(msg + ": equalizer has no effect at 1kHz", energy < refEnergy/4);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } catch (InterruptedException e) {
+ loge(msg, "sleep() interrupted");
+ }
+ finally {
+ releaseEqualizer();
+ if (mp != null) {
+ mp.release();
+ }
+ if (vc != null) {
+ vc.release();
+ }
+ if (probe != null) {
+ probe.release();
+ }
+ am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // private methods
+ //----------------------------------
+
+ private class EnergyProbe {
+ Visualizer mVisualizer = null;
+ private byte[] mFft = new byte[1024];
+
+ public EnergyProbe(int session) {
+ mVisualizer = new Visualizer(session);
+ mVisualizer.setCaptureSize(1024);
+ }
+
+ public int capture(int freq) throws InterruptedException {
+ int energy = 0;
+ int count = 0;
+ if (mVisualizer != null) {
+ mVisualizer.setEnabled(true);
+ for (int i = 0; i < 10; i++) {
+ if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) {
+ // TODO: check speex FFT as it seems to return only the number of points
+ // correspondong to valid part of the spectrum (< Fs).
+ // e.g., if the number of points is 1024, it covers the frequency range
+ // 0 to 22050 instead of 0 to 44100 as expected from an FFT.
+ int bin = freq / (22050 / 1024);
+ int tmp = 0;
+ for (int j = bin-2; j < bin+3; j++) {
+ tmp += (int)mFft[j] * (int)mFft[j];
+ }
+ energy += tmp/5;
+ count++;
+ }
+ Thread.sleep(50);
+ }
+ mVisualizer.setEnabled(false);
+ }
+ if (count == 0) {
+ return 0;
+ }
+ return energy/count;
+ }
+
+ public void release() {
+ if (mVisualizer != null) {
+ mVisualizer.release();
+ mVisualizer = null;
+ }
+ }
+ }
+
+ private void getEqualizer(int session) {
+ if (mEqualizer == null || session != mSession) {
+ if (session != mSession && mEqualizer != null) {
+ mEqualizer.release();
+ mEqualizer = null;
+ }
+ try {
+ mEqualizer = new Equalizer(0, session);
+ mSession = session;
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "getEqualizer() Equalizer not found exception: "+e);
+ } catch (UnsupportedOperationException e) {
+ Log.e(TAG, "getEqualizer() Effect library not loaded exception: "+e);
+ }
+ }
+ assertNotNull("could not create mEqualizer", mEqualizer);
+ }
+
+ private void releaseEqualizer() {
+ if (mEqualizer != null) {
+ mEqualizer.release();
+ mEqualizer = null;
+ }
+ }
+
+}
+
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java
new file mode 100644
index 0000000..6b8ae44
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java
@@ -0,0 +1,339 @@
+/*
+ * 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.mediaframeworktest.functional;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioEffect;
+import android.media.AudioManager;
+import android.media.Virtualizer;
+import android.media.Visualizer;
+import android.media.MediaPlayer;
+
+import android.os.Looper;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+/**
+ * Junit / Instrumentation test case for the media AudioTrack api
+
+ */
+public class MediaVirtualizerTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+ private String TAG = "MediaVirtualizerTest";
+ private final static int MIN_ENERGY_RATIO_2 = 4;
+ private final static short TEST_STRENGTH = 500;
+
+ private Virtualizer mVirtualizer = null;
+ private int mSession = -1;
+
+ public MediaVirtualizerTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ releaseVirtualizer();
+ }
+
+ private static void assumeTrue(String message, boolean cond) {
+ assertTrue("(assume)"+message, cond);
+ }
+
+ private void log(String testName, String message) {
+ Log.v(TAG, "["+testName+"] "+message);
+ }
+
+ private void loge(String testName, String message) {
+ Log.e(TAG, "["+testName+"] "+message);
+ }
+
+ //-----------------------------------------------------------------
+ // VIRTUALIZER TESTS:
+ //----------------------------------
+
+
+ //-----------------------------------------------------------------
+ // 0 - constructor
+ //----------------------------------
+
+ //Test case 0.0: test constructor and release
+ @LargeTest
+ public void test0_0ConstructorAndRelease() throws Exception {
+ boolean result = false;
+ String msg = "test1_0ConstructorAndRelease()";
+ Virtualizer virtualizer = null;
+ try {
+ virtualizer = new Virtualizer(0, 0);
+ assertNotNull(msg + ": could not create Virtualizer", virtualizer);
+ try {
+ assertTrue(msg +": invalid effect ID", (virtualizer.getId() != 0));
+ } catch (IllegalStateException e) {
+ msg = msg.concat(": Virtualizer not initialized");
+ }
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Virtualizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ } finally {
+ if (virtualizer != null) {
+ virtualizer.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+
+ //-----------------------------------------------------------------
+ // 1 - get/set parameters
+ //----------------------------------
+
+ //Test case 1.0: test strength
+ @LargeTest
+ public void test1_0Strength() throws Exception {
+ boolean result = false;
+ String msg = "test1_0Strength()";
+ getVirtualizer(0);
+ try {
+ if (mVirtualizer.getStrengthSupported()) {
+ mVirtualizer.setStrength((short)TEST_STRENGTH);
+ short strength = mVirtualizer.getRoundedStrength();
+ // allow 10% difference between set strength and rounded strength
+ assertTrue(msg +": got incorrect strength",
+ ((float)strength > (float)TEST_STRENGTH * 0.9f) &&
+ ((float)strength < (float)TEST_STRENGTH * 1.1f));
+ } else {
+ short strength = mVirtualizer.getRoundedStrength();
+ assertTrue(msg +": got incorrect strength", strength >= 0 && strength <= 1000);
+ }
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseVirtualizer();
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.1: test properties
+ @LargeTest
+ public void test1_1Properties() throws Exception {
+ boolean result = false;
+ String msg = "test1_1Properties()";
+ getVirtualizer(0);
+ try {
+ Virtualizer.Settings settings = mVirtualizer.getProperties();
+ String str = settings.toString();
+ settings = new Virtualizer.Settings(str);
+ mVirtualizer.setProperties(settings);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseVirtualizer();
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 2 - Effect action
+ //----------------------------------
+
+ //Test case 2.0: test actual virtualizer influence on sound
+ @LargeTest
+ public void test2_0SoundModification() throws Exception {
+ boolean result = false;
+ String msg = "test2_0SoundModification()";
+ EnergyProbe probe = null;
+ AudioEffect vc = null;
+ MediaPlayer mp = null;
+ AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+ int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ am.setStreamVolume(AudioManager.STREAM_MUSIC,
+ am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+ 0);
+
+ try {
+ probe = new EnergyProbe(0);
+ // creating a volume controller on output mix ensures that ro.audio.silent mutes
+ // audio after the effects and not before
+ vc = new AudioEffect(
+ AudioEffect.EFFECT_TYPE_NULL,
+ UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
+ 0,
+ 0);
+ vc.setEnabled(true);
+
+ mp = new MediaPlayer();
+ mp.setDataSource(MediaNames.SINE_200_1000);
+ mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ getVirtualizer(mp.getAudioSessionId());
+ mp.prepare();
+ mp.start();
+ Thread.sleep(200);
+ // measure reference energy around 1kHz
+ int refEnergy200 = probe.capture(200);
+ int refEnergy1000 = probe.capture(1000);
+ mVirtualizer.setStrength((short)1000);
+ mVirtualizer.setEnabled(true);
+ Thread.sleep(500);
+ // measure energy around 1kHz with band level at min
+ int energy200 = probe.capture(200);
+ int energy1000 = probe.capture(1000);
+ // verify that the energy ration between low and high frequencies is at least
+ // four times higher with virtualizer on.
+ // NOTE: this is what is observed with current virtualizer implementation and the test
+ // audio file but is not the primary effect of the virtualizer. A better way would
+ // be to have a stereo PCM capture and check that a strongly paned input is centered
+ // when output. However, we cannot capture stereo with the visualizer.
+ assertTrue(msg + ": virtiualizer has no effect",
+ ((float)energy200/(float)energy1000) >
+ (MIN_ENERGY_RATIO_2 * ((float)refEnergy200/(float)refEnergy1000)));
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } catch (InterruptedException e) {
+ loge(msg, "sleep() interrupted");
+ }
+ finally {
+ releaseVirtualizer();
+ if (mp != null) {
+ mp.release();
+ }
+ if (vc != null) {
+ vc.release();
+ }
+ if (probe != null) {
+ probe.release();
+ }
+ am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+ }
+ assertTrue(msg, result);
+ }
+ //-----------------------------------------------------------------
+ // private methods
+ //----------------------------------
+
+ private class EnergyProbe {
+ Visualizer mVisualizer = null;
+ private byte[] mFft = new byte[1024];
+
+ public EnergyProbe(int session) {
+ mVisualizer = new Visualizer(session);
+ mVisualizer.setCaptureSize(1024);
+ }
+
+ public int capture(int freq) throws InterruptedException {
+ int energy = 0;
+ int count = 0;
+ if (mVisualizer != null) {
+ mVisualizer.setEnabled(true);
+ for (int i = 0; i < 10; i++) {
+ if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) {
+ // TODO: check speex FFT as it seems to return only the number of points
+ // correspondong to valid part of the spectrum (< Fs).
+ // e.g., if the number of points is 1024, it covers the frequency range
+ // 0 to 22050 instead of 0 to 44100 as expected from an FFT.
+ int bin = freq / (22050 / 1024);
+ int tmp = 0;
+ for (int j = bin-2; j < bin+3; j++) {
+ tmp += (int)mFft[j] * (int)mFft[j];
+ }
+ energy += tmp/5;
+ count++;
+ }
+ Thread.sleep(50);
+ }
+ mVisualizer.setEnabled(false);
+ }
+ if (count == 0) {
+ return 0;
+ }
+ return energy/count;
+ }
+
+ public void release() {
+ if (mVisualizer != null) {
+ mVisualizer.release();
+ mVisualizer = null;
+ }
+ }
+ }
+
+ private void getVirtualizer(int session) {
+ if (mVirtualizer == null || session != mSession) {
+ if (session != mSession && mVirtualizer != null) {
+ mVirtualizer.release();
+ mVirtualizer = null;
+ }
+ try {
+ mVirtualizer = new Virtualizer(0, session);
+ mSession = session;
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "getVirtualizer() Virtualizer not found exception: "+e);
+ } catch (UnsupportedOperationException e) {
+ Log.e(TAG, "getVirtualizer() Effect library not loaded exception: "+e);
+ }
+ }
+ assertNotNull("could not create mVirtualizer", mVirtualizer);
+ }
+
+ private void releaseVirtualizer() {
+ if (mVirtualizer != null) {
+ mVirtualizer.release();
+ mVirtualizer = null;
+ }
+ }
+
+}
+
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVisualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVisualizerTest.java
new file mode 100644
index 0000000..26fdbfe
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVisualizerTest.java
@@ -0,0 +1,505 @@
+/*
+ * 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.mediaframeworktest.functional;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioEffect;
+import android.media.AudioManager;
+import android.media.Visualizer;
+import android.media.MediaPlayer;
+
+import android.os.Looper;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+/**
+ * Junit / Instrumentation test case for the media AudioTrack api
+
+ */
+public class MediaVisualizerTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+ private String TAG = "MediaVisualizerTest";
+ private final static int MIN_CAPTURE_RATE_MAX = 20000;
+ private final static int MIN_SAMPLING_RATE = 8000000;
+ private final static int MAX_SAMPLING_RATE = 48000000;
+ private final static int MIN_CAPTURE_SIZE_MAX = 1024;
+ private final static int MAX_CAPTURE_SIZE_MIN = 128;
+
+ private Visualizer mVisualizer = null;
+ private int mSession = -1;
+ private boolean mInitialized = false;
+ private Looper mLooper = null;
+ private final Object lock = new Object();
+ private byte[] mWaveform = null;
+ private byte[] mFft = null;
+ private boolean mCaptureWaveform = false;
+ private boolean mCaptureFft = false;
+
+ public MediaVisualizerTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ releaseVisualizer();
+ }
+
+ private static void assumeTrue(String message, boolean cond) {
+ assertTrue("(assume)"+message, cond);
+ }
+
+ private void log(String testName, String message) {
+ Log.v(TAG, "["+testName+"] "+message);
+ }
+
+ private void loge(String testName, String message) {
+ Log.e(TAG, "["+testName+"] "+message);
+ }
+
+ //-----------------------------------------------------------------
+ // VISUALIZER TESTS:
+ //----------------------------------
+
+
+ //-----------------------------------------------------------------
+ // 0 - constructor
+ //----------------------------------
+
+ //Test case 0.0: test constructor and release
+ @LargeTest
+ public void test0_0ConstructorAndRelease() throws Exception {
+ boolean result = false;
+ String msg = "test1_0ConstructorAndRelease()";
+ Visualizer visualizer = null;
+ try {
+ visualizer = new Visualizer(0);
+ assertNotNull(msg + ": could not create Visualizer", visualizer);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Visualizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ } finally {
+ if (visualizer != null) {
+ visualizer.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+
+ //-----------------------------------------------------------------
+ // 1 - get/set parameters
+ //----------------------------------
+
+ //Test case 1.0: check capture rate and sampling rate
+ @LargeTest
+ public void test1_0CaptureRates() throws Exception {
+ boolean result = false;
+ String msg = "test1_0CaptureRates()";
+ getVisualizer(0);
+ try {
+ int captureRate = mVisualizer.getMaxCaptureRate();
+ assertTrue(msg +": insufficient max capture rate",
+ captureRate >= MIN_CAPTURE_RATE_MAX);
+ int samplingRate = mVisualizer.getSamplingRate();
+ assertTrue(msg +": invalid sampling rate",
+ samplingRate >= MIN_SAMPLING_RATE && samplingRate <= MAX_SAMPLING_RATE);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseVisualizer();
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.1: check capture size
+ @LargeTest
+ public void test1_1CaptureSize() throws Exception {
+ boolean result = false;
+ String msg = "test1_1CaptureSize()";
+ getVisualizer(0);
+ try {
+ int[] range = mVisualizer.getCaptureSizeRange();
+ assertTrue(msg +": insufficient min capture size",
+ range[0] <= MAX_CAPTURE_SIZE_MIN);
+ assertTrue(msg +": insufficient min capture size",
+ range[1] >= MIN_CAPTURE_SIZE_MAX);
+ mVisualizer.setCaptureSize(range[0]);
+ assertEquals(msg +": insufficient min capture size",
+ range[0], mVisualizer.getCaptureSize());
+ mVisualizer.setCaptureSize(range[1]);
+ assertEquals(msg +": insufficient min capture size",
+ range[1], mVisualizer.getCaptureSize());
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseVisualizer();
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 2 - check capture
+ //----------------------------------
+
+ //Test case 2.0: test capture in polling mode
+ @LargeTest
+ public void test2_0PollingCapture() throws Exception {
+ boolean result = false;
+ String msg = "test2_0PollingCapture()";
+ AudioEffect vc = null;
+ MediaPlayer mp = null;
+ AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+ int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ am.setStreamVolume(AudioManager.STREAM_MUSIC,
+ am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+ 0);
+
+ try {
+ // creating a volume controller on output mix ensures that ro.audio.silent mutes
+ // audio after the effects and not before
+ vc = new AudioEffect(
+ AudioEffect.EFFECT_TYPE_NULL,
+ UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
+ 0,
+ 0);
+ vc.setEnabled(true);
+
+ mp = new MediaPlayer();
+ mp.setDataSource(MediaNames.SINE_200_1000);
+ mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ getVisualizer(mp.getAudioSessionId());
+ mVisualizer.setEnabled(true);
+ // check capture on silence
+ byte[] data = new byte[mVisualizer.getCaptureSize()];
+ mVisualizer.getWaveForm(data);
+ int energy = computeEnergy(data, true);
+ assertEquals(msg +": getWaveForm reports energy for silence",
+ 0, energy);
+ mVisualizer.getFft(data);
+ energy = computeEnergy(data, false);
+ assertEquals(msg +": getFft reports energy for silence",
+ 0, energy);
+ mp.prepare();
+ mp.start();
+ Thread.sleep(500);
+ // check capture on sound
+ mVisualizer.getWaveForm(data);
+ energy = computeEnergy(data, true);
+ assertTrue(msg +": getWaveForm reads insufficient level",
+ energy > 0);
+ mVisualizer.getFft(data);
+ energy = computeEnergy(data, false);
+ assertTrue(msg +": getFft reads insufficient level",
+ energy > 0);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } catch (InterruptedException e) {
+ loge(msg, "sleep() interrupted");
+ }
+ finally {
+ releaseVisualizer();
+ if (mp != null) {
+ mp.release();
+ }
+ if (vc != null) {
+ vc.release();
+ }
+ am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 2.1: test capture with listener
+ @LargeTest
+ public void test2_1ListenerCapture() throws Exception {
+ boolean result = false;
+ String msg = "test2_1ListenerCapture()";
+ AudioEffect vc = null;
+ MediaPlayer mp = null;
+ AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+ int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ am.setStreamVolume(AudioManager.STREAM_MUSIC,
+ am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+ 0);
+
+ try {
+ // creating a volume controller on output mix ensures that ro.audio.silent mutes
+ // audio after the effects and not before
+ vc = new AudioEffect(
+ AudioEffect.EFFECT_TYPE_NULL,
+ UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
+ 0,
+ 0);
+ vc.setEnabled(true);
+
+ mp = new MediaPlayer();
+ mp.setDataSource(MediaNames.SINE_200_1000);
+ mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+
+ getVisualizer(mp.getAudioSessionId());
+ createListenerLooper();
+ synchronized(lock) {
+ try {
+ lock.wait(1000);
+ } catch(Exception e) {
+ Log.e(TAG, "Looper creation: wait was interrupted.");
+ }
+ }
+ assertTrue(mInitialized);
+
+ mVisualizer.setEnabled(true);
+
+ // check capture on silence
+ synchronized(lock) {
+ try {
+ mCaptureWaveform = true;
+ lock.wait(1000);
+ mCaptureWaveform = false;
+ } catch(Exception e) {
+ Log.e(TAG, "Capture waveform: wait was interrupted.");
+ }
+ }
+ assertNotNull(msg +": waveform capture failed", mWaveform);
+ int energy = computeEnergy(mWaveform, true);
+ assertEquals(msg +": getWaveForm reports energy for silence",
+ 0, energy);
+
+ synchronized(lock) {
+ try {
+ mCaptureFft = true;
+ lock.wait(1000);
+ mCaptureFft = false;
+ } catch(Exception e) {
+ Log.e(TAG, "Capture FFT: wait was interrupted.");
+ }
+ }
+ assertNotNull(msg +": FFT capture failed", mFft);
+ energy = computeEnergy(mFft, false);
+ assertEquals(msg +": getFft reports energy for silence",
+ 0, energy);
+
+ mp.prepare();
+ mp.start();
+ Thread.sleep(500);
+
+ // check capture on sound
+ synchronized(lock) {
+ try {
+ mCaptureWaveform = true;
+ lock.wait(1000);
+ mCaptureWaveform = false;
+ } catch(Exception e) {
+ Log.e(TAG, "Capture waveform: wait was interrupted.");
+ }
+ }
+ assertNotNull(msg +": waveform capture failed", mWaveform);
+ energy = computeEnergy(mWaveform, true);
+ assertTrue(msg +": getWaveForm reads insufficient level",
+ energy > 0);
+
+ synchronized(lock) {
+ try {
+ mCaptureFft = true;
+ lock.wait(1000);
+ mCaptureFft = false;
+ } catch(Exception e) {
+ Log.e(TAG, "Capture FFT: wait was interrupted.");
+ }
+ }
+ assertNotNull(msg +": FFT capture failed", mFft);
+ energy = computeEnergy(mFft, false);
+ assertTrue(msg +": getFft reads insufficient level",
+ energy > 0);
+
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } catch (InterruptedException e) {
+ loge(msg, "sleep() interrupted");
+ }
+ finally {
+ terminateListenerLooper();
+ releaseVisualizer();
+ if (mp != null) {
+ mp.release();
+ }
+ if (vc != null) {
+ vc.release();
+ }
+ am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // private methods
+ //----------------------------------
+
+ private int computeEnergy(byte[] data, boolean unsigned) {
+ int energy = 0;
+ if (data.length != 0) {
+ for (int i = 0; i < data.length; i++) {
+ int tmp;
+ // convert from unsigned 8 bit to signed 16 bit
+ if (unsigned) {
+ tmp = ((int)data[i] & 0xFF) - 128;
+ } else {
+ tmp = (int)data[i];
+ }
+ energy += tmp*tmp;
+ }
+ energy /= data.length;
+ }
+ return energy;
+ }
+
+ private void getVisualizer(int session) {
+ if (mVisualizer == null || session != mSession) {
+ if (session != mSession && mVisualizer != null) {
+ mVisualizer.release();
+ mVisualizer = null;
+ }
+ try {
+ mVisualizer = new Visualizer(session);
+ mSession = session;
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "getVisualizer() Visualizer not found exception: "+e);
+ } catch (UnsupportedOperationException e) {
+ Log.e(TAG, "getVisualizer() Effect library not loaded exception: "+e);
+ }
+ }
+ assertNotNull("could not create mVisualizer", mVisualizer);
+ }
+
+ private void releaseVisualizer() {
+ if (mVisualizer != null) {
+ mVisualizer.release();
+ mVisualizer = null;
+ }
+ }
+
+ private void createListenerLooper() {
+
+ new Thread() {
+ @Override
+ public void run() {
+ // Set up a looper to be used by mEffect.
+ Looper.prepare();
+
+ // Save the looper so that we can terminate this thread
+ // after we are done with it.
+ mLooper = Looper.myLooper();
+
+ if (mVisualizer != null) {
+ mVisualizer.setDataCaptureListener(new Visualizer.OnDataCaptureListener() {
+ public void onWaveFormDataCapture(
+ Visualizer visualizer, byte[] waveform, int samplingRate) {
+ synchronized(lock) {
+ if (visualizer == mVisualizer) {
+ if (mCaptureWaveform) {
+ mWaveform = waveform;
+ lock.notify();
+ }
+ }
+ }
+ }
+
+ public void onFftDataCapture(
+ Visualizer visualizer, byte[] fft, int samplingRate) {
+ synchronized(lock) {
+ if (visualizer == mVisualizer) {
+ if (mCaptureFft) {
+ mFft = fft;
+ lock.notify();
+ }
+ }
+ }
+ }
+ },
+ 10000,
+ true,
+ true);
+ }
+
+ synchronized(lock) {
+ mInitialized = true;
+ lock.notify();
+ }
+ Looper.loop(); // Blocks forever until Looper.quit() is called.
+ }
+ }.start();
+ }
+ /*
+ * Terminates the listener looper thread.
+ */
+ private void terminateListenerLooper() {
+ if (mLooper != null) {
+ mLooper.quit();
+ mLooper = null;
+ }
+ }
+
+}
+
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/power/MediaPlayerPowerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/power/MediaPlayerPowerTest.java
new file mode 100644
index 0000000..9e91740
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/power/MediaPlayerPowerTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.mediaframeworktest.power;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+import android.media.MediaPlayer;
+import android.os.Environment;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.io.File;
+
+/**
+ * Junit / Instrumentation test case for the power measurment the media player
+ */
+public class MediaPlayerPowerTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+ private String TAG = "MediaPlayerPowerTest";
+ private String MP3_POWERTEST =
+ Environment.getExternalStorageDirectory().toString() + "/power_sample_mp3.mp3";
+ private String MP3_STREAM = "http://75.17.48.204:10088/power_media/power_sample_mp3.mp3";
+ private String OGG_STREAM = "http://75.17.48.204:10088/power_media/power_sample_ogg.mp3";
+ private String AAC_STREAM = "http://75.17.48.204:10088/power_media/power_sample_aac.mp3";
+
+ public MediaPlayerPowerTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ }
+
+ protected void setUp() throws Exception {
+ getActivity();
+ super.setUp();
+
+ }
+
+ public void audioPlayback(String filePath) {
+ try {
+ MediaPlayer mp = new MediaPlayer();
+ mp.setDataSource(filePath);
+ mp.prepare();
+ mp.start();
+ Thread.sleep(200000);
+ mp.stop();
+ mp.release();
+ } catch (Exception e) {
+ Log.v(TAG, e.toString());
+ assertTrue("MP3 Playback", false);
+ }
+ }
+
+ // A very simple test case which start the audio player.
+ // Power measurment will be done in other application.
+ public void testPowerLocalMP3Playback() throws Exception {
+ audioPlayback(MP3_POWERTEST);
+ }
+
+ public void testPowerStreamMP3Playback() throws Exception {
+ audioPlayback(MP3_STREAM);
+ }
+
+ public void testPowerStreamOGGPlayback() throws Exception {
+ audioPlayback(OGG_STREAM);
+ }
+
+ public void testPowerStreamAACPlayback() throws Exception {
+ audioPlayback(AAC_STREAM);
+ }
+}
diff --git a/native/android/Android.mk b/native/android/Android.mk
index 950a1e9..bd2b27a 100644
--- a/native/android/Android.mk
+++ b/native/android/Android.mk
@@ -7,6 +7,7 @@
#
LOCAL_SRC_FILES:= \
asset_manager.cpp \
+ configuration.cpp \
input.cpp \
looper.cpp \
native_activity.cpp \
diff --git a/native/android/asset_manager.cpp b/native/android/asset_manager.cpp
index 36c381e..3f7c1b6 100644
--- a/native/android/asset_manager.cpp
+++ b/native/android/asset_manager.cpp
@@ -17,7 +17,7 @@
#define LOG_TAG "NAsset"
#include <utils/Log.h>
-#include <android/asset_manager.h>
+#include <android/asset_manager_jni.h>
#include <utils/AssetManager.h>
#include <utils/AssetDir.h>
#include <utils/Asset.h>
diff --git a/native/android/configuration.cpp b/native/android/configuration.cpp
new file mode 100644
index 0000000..d76164f
--- /dev/null
+++ b/native/android/configuration.cpp
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Configuration"
+#include <utils/Log.h>
+
+#include <utils/AssetManager.h>
+
+#include <android_runtime/android_content_res_Configuration.h>
+
+using namespace android;
+
+AConfiguration* AConfiguration_new() {
+ AConfiguration* config = new AConfiguration;
+ memset(config, 0, sizeof(AConfiguration));
+ return config;
+}
+
+void AConfiguration_delete(AConfiguration* config) {
+ delete config;
+}
+
+void AConfiguration_fromAssetManager(AConfiguration* out, AAssetManager* am) {
+ ((AssetManager*)am)->getConfiguration(out);
+}
+
+void AConfiguration_copy(AConfiguration* dest, AConfiguration* src) {
+ *dest = *src;
+}
+
+int32_t AConfiguration_getMcc(AConfiguration* config) {
+ return config->mcc;
+}
+
+int32_t AConfiguration_getMnc(AConfiguration* config) {
+ return config->mnc;
+}
+
+void AConfiguration_getLanguage(AConfiguration* config, char* outLanguage) {
+ outLanguage[0] = config->language[0];
+ outLanguage[1] = config->language[1];
+}
+
+void AConfiguration_getCountry(AConfiguration* config, char* outCountry) {
+ outCountry[0] = config->country[0];
+ outCountry[1] = config->country[1];
+}
+
+int32_t AConfiguration_getOrientation(AConfiguration* config) {
+ return config->orientation;
+}
+
+int32_t AConfiguration_getTouchscreen(AConfiguration* config) {
+ return config->touchscreen;
+}
+
+int32_t AConfiguration_getDensity(AConfiguration* config) {
+ return config->density;
+}
+
+int32_t AConfiguration_getKeyboard(AConfiguration* config) {
+ return config->keyboard;
+}
+
+int32_t AConfiguration_getNavigation(AConfiguration* config) {
+ return config->navigation;
+}
+
+int32_t AConfiguration_getKeysHidden(AConfiguration* config) {
+ return config->inputFlags&ResTable_config::MASK_KEYSHIDDEN;
+}
+
+int32_t AConfiguration_getNavHidden(AConfiguration* config) {
+ return (config->inputFlags&ResTable_config::MASK_NAVHIDDEN)
+ >> ResTable_config::SHIFT_NAVHIDDEN;
+}
+
+int32_t AConfiguration_getSdkVersion(AConfiguration* config) {
+ return config->sdkVersion;
+}
+
+int32_t AConfiguration_getScreenSize(AConfiguration* config) {
+ return config->screenLayout&ResTable_config::MASK_SCREENSIZE;
+}
+
+int32_t AConfiguration_getScreenLong(AConfiguration* config) {
+ return (config->screenLayout&ResTable_config::MASK_SCREENLONG)
+ >> ResTable_config::SHIFT_SCREENLONG;
+}
+
+int32_t AConfiguration_getUiModeType(AConfiguration* config) {
+ return config->uiMode&ResTable_config::MASK_UI_MODE_TYPE;
+}
+
+int32_t AConfiguration_getUiModeNight(AConfiguration* config) {
+ return (config->uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
+ >> ResTable_config::SHIFT_UI_MODE_NIGHT;
+
+}
+
+// ----------------------------------------------------------------------
+
+void AConfiguration_setMcc(AConfiguration* config, int32_t mcc) {
+ config->mcc = mcc;
+}
+
+void AConfiguration_setMnc(AConfiguration* config, int32_t mnc) {
+ config->mnc = mnc;
+}
+
+void AConfiguration_setLanguage(AConfiguration* config, const char* language) {
+ config->language[0] = language[0];
+ config->language[1] = language[1];
+}
+
+void AConfiguration_setCountry(AConfiguration* config, const char* country) {
+ config->country[0] = country[0];
+ config->country[1] = country[1];
+}
+
+void AConfiguration_setOrientation(AConfiguration* config, int32_t orientation) {
+ config->orientation = orientation;
+}
+
+void AConfiguration_setTouchscreen(AConfiguration* config, int32_t touchscreen) {
+ config->touchscreen = touchscreen;
+}
+
+void AConfiguration_setDensity(AConfiguration* config, int32_t density) {
+ config->density = density;
+}
+
+void AConfiguration_setKeyboard(AConfiguration* config, int32_t keyboard) {
+ config->keyboard = keyboard;
+}
+
+void AConfiguration_setNavigation(AConfiguration* config, int32_t navigation) {
+ config->navigation = navigation;
+}
+
+void AConfiguration_setKeysHidden(AConfiguration* config, int32_t keysHidden) {
+ config->inputFlags = (config->inputFlags&~ResTable_config::MASK_KEYSHIDDEN)
+ | (keysHidden&ResTable_config::MASK_KEYSHIDDEN);
+}
+
+void AConfiguration_setNavHidden(AConfiguration* config, int32_t navHidden) {
+ config->inputFlags = (config->inputFlags&~ResTable_config::MASK_NAVHIDDEN)
+ | ((navHidden<<ResTable_config::SHIFT_NAVHIDDEN)&ResTable_config::MASK_NAVHIDDEN);
+}
+
+void AConfiguration_setSdkVersion(AConfiguration* config, int32_t sdkVersion) {
+ config->sdkVersion = sdkVersion;
+}
+
+void AConfiguration_setScreenSize(AConfiguration* config, int32_t screenSize) {
+ config->screenLayout = (config->screenLayout&~ResTable_config::MASK_SCREENSIZE)
+ | (screenSize&ResTable_config::MASK_SCREENSIZE);
+}
+
+void AConfiguration_setScreenLong(AConfiguration* config, int32_t screenLong) {
+ config->screenLayout = (config->screenLayout&~ResTable_config::MASK_SCREENLONG)
+ | ((screenLong<<ResTable_config::SHIFT_SCREENLONG)&ResTable_config::MASK_SCREENLONG);
+}
+
+void AConfiguration_setUiModeType(AConfiguration* config, int32_t uiModeType) {
+ config->uiMode = (config->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+ | (uiModeType&ResTable_config::MASK_UI_MODE_TYPE);
+}
+
+void AConfiguration_setUiModeNight(AConfiguration* config, int32_t uiModeNight) {
+ config->uiMode = (config->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
+ | ((uiModeNight<<ResTable_config::SHIFT_UI_MODE_NIGHT)&ResTable_config::MASK_UI_MODE_NIGHT);
+
+}
+
+// ----------------------------------------------------------------------
+
+int32_t AConfiguration_diff(AConfiguration* config1, AConfiguration* config2) {
+ return (config1->diff(*config2));
+}
+
+int32_t AConfiguration_match(AConfiguration* base, AConfiguration* requested) {
+ return base->match(*requested);
+}
+
+int32_t AConfiguration_isBetterThan(AConfiguration* base, AConfiguration* test,
+ AConfiguration* requested) {
+ return base->isBetterThan(*test, requested);
+}
diff --git a/native/copy-to-ndk.sh b/native/copy-to-ndk.sh
new file mode 100644
index 0000000..4f5a16a
--- /dev/null
+++ b/native/copy-to-ndk.sh
@@ -0,0 +1,55 @@
+# Take care of copying current header files over to the correct
+# location in the NDK.
+
+copyndkheaders() {
+ local CURR_PLATFORM=android-9
+ local ALL_PLATFORMS="$CURR_PLATFORM android-8 android-5 android-4 android-3"
+
+ local SRC_HEADERS=$ANDROID_BUILD_TOP/frameworks/base/native/include/android
+ local NDK_PLATFORMS=$ANDROID_BUILD_TOP/development/ndk/platforms
+ local DST_HEADERS=$NDK_PLATFORMS/$CURR_PLATFORM
+
+ local SRC_LIB_ANDROID=$ANDROID_PRODUCT_OUT/system/lib/libandroid.so
+ local DST_LIB_ANDROID=$NDK_PLATFORMS/$CURR_PLATFORM/arch-arm/usr/lib/libandroid.so
+
+ local didsomething=""
+
+ #echo "SRC_HEADERS: $SRC_HEADERS"
+
+ for i in $(cd $SRC_HEADERS; ls *.h); do
+ local src=$SRC_HEADERS/$i
+ local changed=""
+ for j in $ALL_PLATFORMS; do
+ local dst=$NDK_PLATFORMS/$j/arch-arm/usr/include/android/$i
+ if [ "$changed" == "" -a -e $dst ]; then
+ #echo "Exists: $dst"
+ if diff $src $dst >/dev/null; then
+ echo "$i: has not changed from $j" >/dev/null
+ changed="false"
+ else
+ changed="true"
+ echo "$i: has changed from $j" >/dev/null
+ fi
+ fi
+ done
+ if [ "$changed" == "true" -o "$changed" == "" ]; then
+ echo "Updating: $i"
+ cp $src $NDK_PLATFORMS/$CURR_PLATFORM/arch-arm/usr/include/android/$i
+ didsomething="true"
+ fi
+ done
+
+ if diff $SRC_LIB_ANDROID $DST_LIB_ANDROID >/dev/null; then
+ echo "libandroid.so: has not changed" >/dev/null
+ else
+ echo "Updating: $DST_LIB_ANDROID"
+ cp $SRC_LIB_ANDROID $DST_LIB_ANDROID
+ didsomething="true"
+ fi
+ if [ "$didsomething" != "" ]; then
+ echo "Headers changed... rebuilding platforms."
+ sh $ANDROID_BUILD_TOP/ndk/build/tools/build-platforms.sh
+ fi
+}
+
+copyndkheaders
diff --git a/native/include/android/asset_manager.h b/native/include/android/asset_manager.h
index 89989f8..4fa0ef3 100644
--- a/native/include/android/asset_manager.h
+++ b/native/include/android/asset_manager.h
@@ -18,8 +18,6 @@
#ifndef ANDROID_ASSET_MANAGER_H
#define ANDROID_ASSET_MANAGER_H
-#include <jni.h>
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -43,14 +41,6 @@
/**
- * Given a Dalvik AssetManager object, obtain the corresponding native AAssetManager
- * object. Note that the caller is responsible for obtaining and holding a VM reference
- * to the jobject to prevent its being garbage collected while the native object is
- * in use.
- */
-AAssetManager* AAssetManager_fromJava(JNIEnv* env, jobject assetManager);
-
-/**
* Open the named directory within the asset hierarchy. The directory can then
* be inspected with the AAssetDir functions. To open the top-level directory,
* pass in "" as the dirName.
diff --git a/native/include/android/asset_manager_jni.h b/native/include/android/asset_manager_jni.h
new file mode 100644
index 0000000..aec2d3c
--- /dev/null
+++ b/native/include/android/asset_manager_jni.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+
+#ifndef ANDROID_ASSET_MANAGER_JNI_H
+#define ANDROID_ASSET_MANAGER_JNI_H
+
+#include <android/asset_manager.h>
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Given a Dalvik AssetManager object, obtain the corresponding native AAssetManager
+ * object. Note that the caller is responsible for obtaining and holding a VM reference
+ * to the jobject to prevent its being garbage collected while the native object is
+ * in use.
+ */
+AAssetManager* AAssetManager_fromJava(JNIEnv* env, jobject assetManager);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // ANDROID_ASSET_MANAGER_JNI_H
diff --git a/native/include/android/configuration.h b/native/include/android/configuration.h
new file mode 100644
index 0000000..79b9b1e
--- /dev/null
+++ b/native/include/android/configuration.h
@@ -0,0 +1,319 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_CONFIGURATION_H
+#define ANDROID_CONFIGURATION_H
+
+#include <android/asset_manager.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct AConfiguration;
+typedef struct AConfiguration AConfiguration;
+
+enum {
+ ACONFIGURATION_ORIENTATION_ANY = 0x0000,
+ ACONFIGURATION_ORIENTATION_PORT = 0x0001,
+ ACONFIGURATION_ORIENTATION_LAND = 0x0002,
+ ACONFIGURATION_ORIENTATION_SQUARE = 0x0003,
+
+ ACONFIGURATION_TOUCHSCREEN_ANY = 0x0000,
+ ACONFIGURATION_TOUCHSCREEN_NOTOUCH = 0x0001,
+ ACONFIGURATION_TOUCHSCREEN_STYLUS = 0x0002,
+ ACONFIGURATION_TOUCHSCREEN_FINGER = 0x0003,
+
+ ACONFIGURATION_DENSITY_DEFAULT = 0,
+ ACONFIGURATION_DENSITY_LOW = 120,
+ ACONFIGURATION_DENSITY_MEDIUM = 160,
+ ACONFIGURATION_DENSITY_HIGH = 240,
+ ACONFIGURATION_DENSITY_NONE = 0xffff,
+
+ ACONFIGURATION_KEYBOARD_ANY = 0x0000,
+ ACONFIGURATION_KEYBOARD_NOKEYS = 0x0001,
+ ACONFIGURATION_KEYBOARD_QWERTY = 0x0002,
+ ACONFIGURATION_KEYBOARD_12KEY = 0x0003,
+
+ ACONFIGURATION_NAVIGATION_ANY = 0x0000,
+ ACONFIGURATION_NAVIGATION_NONAV = 0x0001,
+ ACONFIGURATION_NAVIGATION_DPAD = 0x0002,
+ ACONFIGURATION_NAVIGATION_TRACKBALL = 0x0003,
+ ACONFIGURATION_NAVIGATION_WHEEL = 0x0004,
+
+ ACONFIGURATION_KEYSHIDDEN_ANY = 0x0000,
+ ACONFIGURATION_KEYSHIDDEN_NO = 0x0001,
+ ACONFIGURATION_KEYSHIDDEN_YES = 0x0002,
+ ACONFIGURATION_KEYSHIDDEN_SOFT = 0x0003,
+
+ ACONFIGURATION_NAVHIDDEN_ANY = 0x0000,
+ ACONFIGURATION_NAVHIDDEN_NO = 0x0001,
+ ACONFIGURATION_NAVHIDDEN_YES = 0x0002,
+
+ ACONFIGURATION_SCREENSIZE_ANY = 0x00,
+ ACONFIGURATION_SCREENSIZE_SMALL = 0x01,
+ ACONFIGURATION_SCREENSIZE_NORMAL = 0x02,
+ ACONFIGURATION_SCREENSIZE_LARGE = 0x03,
+ ACONFIGURATION_SCREENSIZE_XLARGE = 0x04,
+
+ ACONFIGURATION_SCREENLONG_ANY = 0x00,
+ ACONFIGURATION_SCREENLONG_NO = 0x1,
+ ACONFIGURATION_SCREENLONG_YES = 0x2,
+
+ ACONFIGURATION_UI_MODE_TYPE_ANY = 0x00,
+ ACONFIGURATION_UI_MODE_TYPE_NORMAL = 0x01,
+ ACONFIGURATION_UI_MODE_TYPE_DESK = 0x02,
+ ACONFIGURATION_UI_MODE_TYPE_CAR = 0x03,
+
+ ACONFIGURATION_UI_MODE_NIGHT_ANY = 0x00,
+ ACONFIGURATION_UI_MODE_NIGHT_NO = 0x10,
+ ACONFIGURATION_UI_MODE_NIGHT_YES = 0x20,
+
+ ACONFIGURATION_MCC = 0x0001,
+ ACONFIGURATION_MNC = 0x0002,
+ ACONFIGURATION_LOCALE = 0x0004,
+ ACONFIGURATION_TOUCHSCREEN = 0x0008,
+ ACONFIGURATION_KEYBOARD = 0x0010,
+ ACONFIGURATION_KEYBOARD_HIDDEN = 0x0020,
+ ACONFIGURATION_NAVIGATION = 0x0040,
+ ACONFIGURATION_ORIENTATION = 0x0080,
+ ACONFIGURATION_DENSITY = 0x0100,
+ ACONFIGURATION_SCREEN_SIZE = 0x0200,
+ ACONFIGURATION_VERSION = 0x0400,
+ ACONFIGURATION_SCREEN_LAYOUT = 0x0800,
+ ACONFIGURATION_UI_MODE = 0x1000,
+};
+
+/**
+ * Create a new AConfiguration, initialized with no values set.
+ */
+AConfiguration* AConfiguration_new();
+
+/**
+ * Free an AConfiguration that was previously created with
+ * AConfiguration_new().
+ */
+void AConfiguration_delete(AConfiguration* config);
+
+/**
+ * Create and return a new AConfiguration based on the current configuration in
+ * use in the given AssetManager.
+ */
+void AConfiguration_fromAssetManager(AConfiguration* out, AAssetManager* am);
+
+/**
+ * Copy the contents of 'src' to 'dest'.
+ */
+void AConfiguration_copy(AConfiguration* dest, AConfiguration* src);
+
+/**
+ * Return the current MCC set in the configuration. 0 if not set.
+ */
+int32_t AConfiguration_getMcc(AConfiguration* config);
+
+/**
+ * Set the current MCC in the configuration. 0 to clear.
+ */
+void AConfiguration_setMcc(AConfiguration* config, int32_t mcc);
+
+/**
+ * Return the current MNC set in the configuration. 0 if not set.
+ */
+int32_t AConfiguration_getMnc(AConfiguration* config);
+
+/**
+ * Set the current MNC in the configuration. 0 to clear.
+ */
+void AConfiguration_setMnc(AConfiguration* config, int32_t mnc);
+
+/**
+ * Return the current language code set in the configuration. The output will
+ * be filled with an array of two characters. They are not 0-terminated. If
+ * a language is not set, they will be 0.
+ */
+void AConfiguration_getLanguage(AConfiguration* config, char* outLanguage);
+
+/**
+ * Set the current language code in the configuration, from the first two
+ * characters in the string.
+ */
+void AConfiguration_setLanguage(AConfiguration* config, const char* language);
+
+/**
+ * Return the current country code set in the configuration. The output will
+ * be filled with an array of two characters. They are not 0-terminated. If
+ * a country is not set, they will be 0.
+ */
+void AConfiguration_getCountry(AConfiguration* config, char* outCountry);
+
+/**
+ * Set the current country code in the configuration, from the first two
+ * characters in the string.
+ */
+void AConfiguration_setCountry(AConfiguration* config, const char* country);
+
+/**
+ * Return the current ACONFIGURATION_ORIENTATION_* set in the configuration.
+ */
+int32_t AConfiguration_getOrientation(AConfiguration* config);
+
+/**
+ * Set the current orientation in the configuration.
+ */
+void AConfiguration_setOrientation(AConfiguration* config, int32_t orientation);
+
+/**
+ * Return the current ACONFIGURATION_TOUCHSCREEN_* set in the configuration.
+ */
+int32_t AConfiguration_getTouchscreen(AConfiguration* config);
+
+/**
+ * Set the current touchscreen in the configuration.
+ */
+void AConfiguration_setTouchscreen(AConfiguration* config, int32_t touchscreen);
+
+/**
+ * Return the current ACONFIGURATION_DENSITY_* set in the configuration.
+ */
+int32_t AConfiguration_getDensity(AConfiguration* config);
+
+/**
+ * Set the current density in the configuration.
+ */
+void AConfiguration_setDensity(AConfiguration* config, int32_t density);
+
+/**
+ * Return the current ACONFIGURATION_KEYBOARD_* set in the configuration.
+ */
+int32_t AConfiguration_getKeyboard(AConfiguration* config);
+
+/**
+ * Set the current keyboard in the configuration.
+ */
+void AConfiguration_setKeyboard(AConfiguration* config, int32_t keyboard);
+
+/**
+ * Return the current ACONFIGURATION_NAVIGATION_* set in the configuration.
+ */
+int32_t AConfiguration_getNavigation(AConfiguration* config);
+
+/**
+ * Set the current navigation in the configuration.
+ */
+void AConfiguration_setNavigation(AConfiguration* config, int32_t navigation);
+
+/**
+ * Return the current ACONFIGURATION_KEYSHIDDEN_* set in the configuration.
+ */
+int32_t AConfiguration_getKeysHidden(AConfiguration* config);
+
+/**
+ * Set the current keys hidden in the configuration.
+ */
+void AConfiguration_setKeysHidden(AConfiguration* config, int32_t keysHidden);
+
+/**
+ * Return the current ACONFIGURATION_NAVHIDDEN_* set in the configuration.
+ */
+int32_t AConfiguration_getNavHidden(AConfiguration* config);
+
+/**
+ * Set the current nav hidden in the configuration.
+ */
+void AConfiguration_setNavHidden(AConfiguration* config, int32_t navHidden);
+
+/**
+ * Return the current SDK (API) version set in the configuration.
+ */
+int32_t AConfiguration_getSdkVersion(AConfiguration* config);
+
+/**
+ * Set the current SDK version in the configuration.
+ */
+void AConfiguration_setSdkVersion(AConfiguration* config, int32_t sdkVersion);
+
+/**
+ * Return the current ACONFIGURATION_SCREENSIZE_* set in the configuration.
+ */
+int32_t AConfiguration_getScreenSize(AConfiguration* config);
+
+/**
+ * Set the current screen size in the configuration.
+ */
+void AConfiguration_setScreenSize(AConfiguration* config, int32_t screenSize);
+
+/**
+ * Return the current ACONFIGURATION_SCREENLONG_* set in the configuration.
+ */
+int32_t AConfiguration_getScreenLong(AConfiguration* config);
+
+/**
+ * Set the current screen long in the configuration.
+ */
+void AConfiguration_setScreenLong(AConfiguration* config, int32_t screenLong);
+
+/**
+ * Return the current ACONFIGURATION_UI_MODE_TYPE_* set in the configuration.
+ */
+int32_t AConfiguration_getUiModeType(AConfiguration* config);
+
+/**
+ * Set the current UI mode type in the configuration.
+ */
+void AConfiguration_setUiModeType(AConfiguration* config, int32_t uiModeType);
+
+/**
+ * Return the current ACONFIGURATION_UI_MODE_NIGHT_* set in the configuration.
+ */
+int32_t AConfiguration_getUiModeNight(AConfiguration* config);
+
+/**
+ * Set the current UI mode night in the configuration.
+ */
+void AConfiguration_setUiModeNight(AConfiguration* config, int32_t uiModeNight);
+
+/**
+ * Perform a diff between two configurations. Returns a bit mask of
+ * ACONFIGURATION_* constants, each bit set meaning that configuration element
+ * is different between them.
+ */
+int32_t AConfiguration_diff(AConfiguration* config1, AConfiguration* config2);
+
+/**
+ * Determine whether 'base' is a valid configuration for use within the
+ * environment 'requested'. Returns 0 if there are any values in 'base'
+ * that conflict with 'requested'. Returns 1 if it does not conflict.
+ */
+int32_t AConfiguration_match(AConfiguration* base, AConfiguration* requested);
+
+/**
+ * Determine whether the configuration in 'test' is better than the existing
+ * configuration in 'base'. If 'requested' is non-NULL, this decision is based
+ * on the overall configuration given there. If it is NULL, this decision is
+ * simply based on which configuration is more specific. Returns non-0 if
+ * 'test' is better than 'base'.
+ *
+ * This assumes you have already filtered the configurations with
+ * AConfiguration_match().
+ */
+int32_t AConfiguration_isBetterThan(AConfiguration* base, AConfiguration* test,
+ AConfiguration* requested);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // ANDROID_CONFIGURATION_H
diff --git a/native/include/android/native_activity.h b/native/include/android/native_activity.h
index ee4204d..d74e1ce 100644
--- a/native/include/android/native_activity.h
+++ b/native/include/android/native_activity.h
@@ -197,6 +197,12 @@
void (*onContentRectChanged)(ANativeActivity* activity, const ARect* rect);
/**
+ * The current device AConfiguration has changed. The new configuration can
+ * be retrieved from assetManager.
+ */
+ void (*onConfigurationChanged)(ANativeActivity* activity);
+
+ /**
* The system is running low on memory. Use this callback to release
* resources you do not need, to help the system avoid killing more
* important processes.
@@ -208,7 +214,9 @@
* This is the function that must be in the native code to instantiate the
* application's native activity. It is called with the activity instance (see
* above); if the code is being instantiated from a previously saved instance,
- * the savedState will be non-NULL and point to the saved data.
+ * the savedState will be non-NULL and point to the saved data. You must make
+ * any copy of this data you need -- it will be released after you return from
+ * this function.
*/
typedef void ANativeActivity_createFunc(ANativeActivity* activity,
void* savedState, size_t savedStateSize);
diff --git a/native/include/android/native_window.h b/native/include/android/native_window.h
index 7599d7e..ad03d0e 100644
--- a/native/include/android/native_window.h
+++ b/native/include/android/native_window.h
@@ -36,12 +36,23 @@
typedef struct ANativeWindow ANativeWindow;
typedef struct ANativeWindow_Buffer {
+ // The number of pixels that are show horizontally.
int32_t width;
+
+ // The number of pixels that are shown vertically.
int32_t height;
+
+ // The number of *pixels* that a line in the buffer takes in
+ // memory. This may be >= width.
int32_t stride;
+
+ // The format of the buffer. One of WINDOW_FORMAT_*
int32_t format;
+
+ // The actual bits.
void* bits;
+ // Do not touch.
uint32_t reserved[6];
} ANativeWindow_Buffer;
diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp
index 23837ef..dcf8735c 100644
--- a/opengl/libs/EGL/getProcAddress.cpp
+++ b/opengl/libs/EGL/getProcAddress.cpp
@@ -27,9 +27,12 @@
// ----------------------------------------------------------------------------
#undef API_ENTRY
-#undef CALL_GL_API
+#undef CALL_GL_EXTENSION_API
#undef GL_EXTENSION
#undef GL_EXTENSION_NAME
+#undef GL_EXTENSION_ARRAY
+#undef GL_EXTENSION_LIST
+#undef GET_TLS
#if defined(__arm__)
@@ -60,7 +63,7 @@
: \
);
- #define GL_EXTENSION_NAME(_n) __glExtFwd##_n
+ #define GL_EXTENSION_NAME(_n) __glExtFwd##_n
#define GL_EXTENSION(_n) \
void API_ENTRY(GL_EXTENSION_NAME(_n))() { \
@@ -78,97 +81,40 @@
#endif
-GL_EXTENSION(0)
-GL_EXTENSION(1)
-GL_EXTENSION(2)
-GL_EXTENSION(3)
-GL_EXTENSION(4)
-GL_EXTENSION(5)
-GL_EXTENSION(6)
-GL_EXTENSION(7)
-GL_EXTENSION(8)
-GL_EXTENSION(9)
-GL_EXTENSION(10)
-GL_EXTENSION(11)
-GL_EXTENSION(12)
-GL_EXTENSION(13)
-GL_EXTENSION(14)
-GL_EXTENSION(15)
+#define GL_EXTENSION_LIST(name) \
+ name(0) name(1) name(2) name(3) \
+ name(4) name(5) name(6) name(7) \
+ name(8) name(9) name(10) name(11) \
+ name(12) name(13) name(14) name(15) \
+ name(16) name(17) name(18) name(19) \
+ name(20) name(21) name(22) name(23) \
+ name(24) name(25) name(26) name(27) \
+ name(28) name(29) name(30) name(31) \
+ name(32) name(33) name(34) name(35) \
+ name(36) name(37) name(38) name(39) \
+ name(40) name(41) name(42) name(43) \
+ name(44) name(45) name(46) name(47) \
+ name(48) name(49) name(50) name(51) \
+ name(52) name(53) name(54) name(55) \
+ name(56) name(57) name(58) name(59) \
+ name(60) name(61) name(62) name(63)
-GL_EXTENSION(16)
-GL_EXTENSION(17)
-GL_EXTENSION(18)
-GL_EXTENSION(19)
-GL_EXTENSION(20)
-GL_EXTENSION(21)
-GL_EXTENSION(22)
-GL_EXTENSION(23)
-GL_EXTENSION(24)
-GL_EXTENSION(25)
-GL_EXTENSION(26)
-GL_EXTENSION(27)
-GL_EXTENSION(28)
-GL_EXTENSION(29)
-GL_EXTENSION(30)
-GL_EXTENSION(31)
-GL_EXTENSION(32)
-GL_EXTENSION(33)
-GL_EXTENSION(34)
-GL_EXTENSION(35)
-GL_EXTENSION(36)
-GL_EXTENSION(37)
-GL_EXTENSION(38)
-GL_EXTENSION(39)
-GL_EXTENSION(40)
-GL_EXTENSION(41)
-GL_EXTENSION(42)
-GL_EXTENSION(43)
-GL_EXTENSION(44)
-GL_EXTENSION(45)
-GL_EXTENSION(46)
-GL_EXTENSION(47)
+GL_EXTENSION_LIST( GL_EXTENSION )
-GL_EXTENSION(48)
-GL_EXTENSION(49)
-GL_EXTENSION(50)
-GL_EXTENSION(51)
-GL_EXTENSION(52)
-GL_EXTENSION(53)
-GL_EXTENSION(54)
-GL_EXTENSION(55)
-GL_EXTENSION(56)
-GL_EXTENSION(57)
-GL_EXTENSION(58)
-GL_EXTENSION(59)
-GL_EXTENSION(60)
-GL_EXTENSION(61)
-GL_EXTENSION(62)
-GL_EXTENSION(63)
+#define GL_EXTENSION_ARRAY(_n) GL_EXTENSION_NAME(_n),
extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS] = {
- GL_EXTENSION_NAME(0), GL_EXTENSION_NAME(1), GL_EXTENSION_NAME(2), GL_EXTENSION_NAME(3),
- GL_EXTENSION_NAME(4), GL_EXTENSION_NAME(5), GL_EXTENSION_NAME(6), GL_EXTENSION_NAME(7),
- GL_EXTENSION_NAME(8), GL_EXTENSION_NAME(9), GL_EXTENSION_NAME(10), GL_EXTENSION_NAME(11),
- GL_EXTENSION_NAME(12), GL_EXTENSION_NAME(13), GL_EXTENSION_NAME(14), GL_EXTENSION_NAME(15),
- GL_EXTENSION_NAME(16), GL_EXTENSION_NAME(17), GL_EXTENSION_NAME(18), GL_EXTENSION_NAME(19),
- GL_EXTENSION_NAME(20), GL_EXTENSION_NAME(21), GL_EXTENSION_NAME(22), GL_EXTENSION_NAME(23),
- GL_EXTENSION_NAME(24), GL_EXTENSION_NAME(25), GL_EXTENSION_NAME(26), GL_EXTENSION_NAME(27),
- GL_EXTENSION_NAME(28), GL_EXTENSION_NAME(29), GL_EXTENSION_NAME(30), GL_EXTENSION_NAME(31),
- GL_EXTENSION_NAME(32), GL_EXTENSION_NAME(33), GL_EXTENSION_NAME(34), GL_EXTENSION_NAME(35),
- GL_EXTENSION_NAME(36), GL_EXTENSION_NAME(37), GL_EXTENSION_NAME(38), GL_EXTENSION_NAME(39),
- GL_EXTENSION_NAME(40), GL_EXTENSION_NAME(41), GL_EXTENSION_NAME(42), GL_EXTENSION_NAME(43),
- GL_EXTENSION_NAME(44), GL_EXTENSION_NAME(45), GL_EXTENSION_NAME(46), GL_EXTENSION_NAME(47),
- GL_EXTENSION_NAME(48), GL_EXTENSION_NAME(49), GL_EXTENSION_NAME(50), GL_EXTENSION_NAME(51),
- GL_EXTENSION_NAME(52), GL_EXTENSION_NAME(53), GL_EXTENSION_NAME(54), GL_EXTENSION_NAME(55),
- GL_EXTENSION_NAME(56), GL_EXTENSION_NAME(57), GL_EXTENSION_NAME(58), GL_EXTENSION_NAME(59),
- GL_EXTENSION_NAME(60), GL_EXTENSION_NAME(61), GL_EXTENSION_NAME(62), GL_EXTENSION_NAME(63)
+ GL_EXTENSION_LIST( GL_EXTENSION_ARRAY )
};
+#undef GET_TLS
+#undef GL_EXTENSION_LIST
+#undef GL_EXTENSION_ARRAY
#undef GL_EXTENSION_NAME
#undef GL_EXTENSION
#undef API_ENTRY
-#undef CALL_GL_API
+#undef CALL_GL_EXTENSION_API
// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp
index 924737e..a12edf2 100644
--- a/opengl/libs/GLES2/gl2.cpp
+++ b/opengl/libs/GLES2/gl2.cpp
@@ -39,6 +39,8 @@
#undef CALL_GL_API
#undef CALL_GL_API_RETURN
+#define DEBUG_CALL_GL_API 0
+
#if USE_FAST_TLS_KEY
#ifdef HAVE_ARM_TLS_REGISTER
@@ -73,10 +75,24 @@
#define API_ENTRY(_api) _api
+#if DEBUG_CALL_GL_API
+
#define CALL_GL_API(_api, ...) \
gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \
- _c->_api(__VA_ARGS__)
-
+ _c->_api(__VA_ARGS__); \
+ GLenum status = GL_NO_ERROR; \
+ while ((status = glGetError()) != GL_NO_ERROR) { \
+ LOGD("[" #_api "] 0x%x", status); \
+ }
+
+#else
+
+ #define CALL_GL_API(_api, ...) \
+ gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \
+ _c->_api(__VA_ARGS__);
+
+#endif
+
#define CALL_GL_API_RETURN(_api, ...) \
gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \
return _c->_api(__VA_ARGS__)
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_expand_default.png b/packages/SystemUI/res/drawable-hdpi/status_bar_expand_default.png
new file mode 100644
index 0000000..92fc7f8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_expand_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_expand_pressed.png b/packages/SystemUI/res/drawable-hdpi/status_bar_expand_pressed.png
new file mode 100644
index 0000000..77f09ac
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_expand_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_expand_default.png b/packages/SystemUI/res/drawable-mdpi/status_bar_expand_default.png
new file mode 100644
index 0000000..4d19717
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_expand_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_expand_pressed.png b/packages/SystemUI/res/drawable-mdpi/status_bar_expand_pressed.png
new file mode 100644
index 0000000..830cbd5
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_expand_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/status_bar_expand.xml b/packages/SystemUI/res/drawable/status_bar_expand.xml
new file mode 100644
index 0000000..f966920
--- /dev/null
+++ b/packages/SystemUI/res/drawable/status_bar_expand.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true" android:drawable="@drawable/status_bar_expand_pressed" />
+ <item android:drawable="@drawable/status_bar_expand_default" />
+</selector>
+
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar.xml b/packages/SystemUI/res/layout-xlarge/status_bar.xml
index 9ae2c25..ffb1571 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar.xml
@@ -39,13 +39,13 @@
android:onClick="notificationIconsClicked"
android:background="@drawable/status_bar_icon_tray"
>
- <view
+ <ImageView
class="com.android.systemui.statusbar.tablet.NotificationIconArea$MoreView"
- android:id="@+id/more"
+ android:id="@+id/expand"
android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:src="@drawable/stat_notify_more"
- android:layout_marginLeft="10dip"
+ android:layout_height="wrap_content"
+ android:src="@drawable/status_bar_expand"
+ android:onClick="notificationIconsClicked"
/>
<view
class="com.android.systemui.statusbar.tablet.NotificationIconArea$IconLayout"
@@ -64,39 +64,47 @@
</com.android.systemui.statusbar.tablet.NotificationIconArea>
- <RelativeLayout android:id="@+id/systemInfo"
- android:layout_width="200dip"
+ <LinearLayout android:id="@+id/ticker"
+ android:layout_width="300dip"
android:layout_height="match_parent"
- android:layout_centerHorizontal="true"
- android:clickable="true"
- android:onClick="systemInfoClicked"
+ android:paddingLeft="6dip"
+ android:animationCache="false"
+ android:layout_alignLeft="@id/notificationIcons"
+ android:layout_alignTop="@id/notificationIcons"
+ android:orientation="horizontal"
+ android:visibility="gone"
>
- <com.android.systemui.statusbar.Clock
- android:id="@+id/clock"
- android:textAppearance="@*android:style/TextAppearance.StatusBar.Icon"
+ <ImageView android:id="@+id/tickerIcon"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerInParent="true"
- android:singleLine="true"
- android:textSize="16sp"
- android:textStyle="bold"
- android:padding="6dip"
+ android:layout_height="match_parent"
+ android:layout_marginRight="8dip"
/>
- <ImageView
- android:layout_width="wrap_content"
+ <com.android.systemui.statusbar.TickerView android:id="@+id/tickerText"
+ android:layout_width="0dip"
+ android:layout_weight="1"
android:layout_height="wrap_content"
- android:layout_toLeftOf="@id/clock"
- android:layout_centerVertical="true"
- android:src="@drawable/dots_empty"
- />
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/clock"
- android:layout_centerVertical="true"
- android:src="@drawable/dots_full"
- />
- </RelativeLayout>
+ android:paddingTop="2dip"
+ android:paddingRight="10dip">
+ <TextView
+ android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ />
+ <TextView
+ android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ />
+ </com.android.systemui.statusbar.TickerView>
+ </LinearLayout>
+
+ <include layout="@layout/status_bar_center"
+ android:layout_width="100dip"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ />
<com.android.systemui.statusbar.KeyButtonView android:id="@+id/back"
android:layout_width="wrap_content"
@@ -125,60 +133,5 @@
android:src="@drawable/status_bar_home"
systemui:keyCode="3"
/>
-
-<!--
-
- <LinearLayout android:id="@+id/ticker"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingLeft="6dip"
- android:animationCache="false"
- android:orientation="horizontal" >
- <ImageSwitcher android:id="@+id/tickerIcon"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginRight="8dip"
- >
- <com.android.systemui.statusbar.AnimatedImageView
- android:layout_width="25dip"
- android:layout_height="25dip"
- />
- <com.android.systemui.statusbar.AnimatedImageView
- android:layout_width="25dip"
- android:layout_height="25dip"
- />
- </ImageSwitcher>
- <com.android.systemui.statusbar.TickerView android:id="@+id/tickerText"
- android:layout_width="0dip"
- android:layout_weight="1"
- android:layout_height="wrap_content"
- android:paddingTop="2dip"
- android:paddingRight="10dip">
- <TextView
- android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- />
- <TextView
- android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- />
- </com.android.systemui.statusbar.TickerView>
- </LinearLayout>
-
- <com.android.systemui.statusbar.DateView android:id="@+id/date"
- android:textAppearance="@*android:style/TextAppearance.StatusBar.Icon"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:singleLine="true"
- android:gravity="center_vertical|left"
- android:paddingLeft="6px"
- android:paddingRight="6px"
- android:background="@drawable/status_bar_background"
- />
--->
</RelativeLayout>
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_center.xml b/packages/SystemUI/res/layout-xlarge/status_bar_center.xml
new file mode 100644
index 0000000..c32e997
--- /dev/null
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_center.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- Center of status bar: System info display, system info panel trigger -->
+<RelativeLayout android:id="@+id/systemInfo"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
+ android:layout_width="100dip"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:clickable="true"
+ android:onClick="systemInfoClicked"
+ >
+ <com.android.systemui.statusbar.Clock
+ style="@*android:style/TextAppearance.StatusBar.Icon"
+ android:id="@+id/clock"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:gravity="center"
+ android:textSize="16sp"
+ android:textStyle="bold"
+ android:padding="2dip"
+ />
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_below="@id/clock"
+ android:src="@drawable/dots_empty"
+ />
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/clock"
+ android:src="@drawable/dots_full"
+ />
+</RelativeLayout>
+
+
diff --git a/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml b/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml
index 84a718a..3489eec 100644
--- a/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml
+++ b/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml
@@ -46,17 +46,23 @@
android:background="@android:drawable/divider_horizontal_dark"
/>
- <LinearLayout
- android:id="@+id/content"
- android:layout_width="match_parent"
+ <ScrollView
+ android:id="@+id/notificationScroller"
android:layout_height="wrap_content"
- android:gravity="center_horizontal|bottom"
- android:animationCache="false"
- android:orientation="vertical"
- android:background="@drawable/status_bar_background"
- android:clickable="true"
- android:focusable="true"
- android:descendantFocusability="afterDescendants"
+ android:layout_width="match_parent"
>
- </LinearLayout>
+ <LinearLayout
+ android:id="@+id/content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal|bottom"
+ android:animationCache="false"
+ android:orientation="vertical"
+ android:background="@drawable/status_bar_background"
+ android:clickable="true"
+ android:focusable="true"
+ android:descendantFocusability="afterDescendants"
+ >
+ </LinearLayout>
+ </ScrollView>
</LinearLayout>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
index 5add6de..91b583b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
@@ -1086,12 +1086,12 @@
}
private class MyTicker extends Ticker {
- MyTicker(Context context, StatusBarView sb) {
+ MyTicker(Context context, View sb) {
super(context, sb);
}
@Override
- void tickerStarting() {
+ public void tickerStarting() {
mTicking = true;
mIcons.setVisibility(View.GONE);
mTickerView.setVisibility(View.VISIBLE);
@@ -1103,7 +1103,7 @@
}
@Override
- void tickerDone() {
+ public void tickerDone() {
mIcons.setVisibility(View.VISIBLE);
mTickerView.setVisibility(View.GONE);
mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
@@ -1114,7 +1114,7 @@
}
}
- void tickerHalting() {
+ public void tickerHalting() {
mIcons.setVisibility(View.VISIBLE);
mTickerView.setVisibility(View.GONE);
mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java b/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java
index 07e8653..0aaa370 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java
@@ -141,7 +141,7 @@
}
};
- Ticker(Context context, StatusBarView sb) {
+ public Ticker(Context context, View sb) {
mContext = context;
mTickerView = sb.findViewById(R.id.ticker);
@@ -163,7 +163,7 @@
}
- void addEntry(StatusBarNotification n) {
+ public void addEntry(StatusBarNotification n) {
int initialCount = mSegments.size();
// If what's being displayed has the same text and icon, just drop it
@@ -212,7 +212,7 @@
}
}
- void removeEntry(StatusBarNotification n) {
+ public void removeEntry(StatusBarNotification n) {
for (int i=mSegments.size()-1; i>=0; i--) {
Segment seg = mSegments.get(i);
if (n.id == seg.notification.id && n.pkg.equals(seg.notification.pkg)) {
@@ -221,13 +221,13 @@
}
}
- void halt() {
+ public void halt() {
mHandler.removeCallbacks(mAdvanceTicker);
mSegments.clear();
tickerHalting();
}
- void reflowText() {
+ public void reflowText() {
if (mSegments.size() > 0) {
Segment seg = mSegments.get(0);
CharSequence text = seg.getText();
@@ -266,8 +266,8 @@
mHandler.postDelayed(mAdvanceTicker, TICKER_SEGMENT_DELAY);
}
- abstract void tickerStarting();
- abstract void tickerDone();
- abstract void tickerHalting();
+ public abstract void tickerStarting();
+ public abstract void tickerDone();
+ public abstract void tickerHalting();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/TickerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/TickerView.java
index 9749ae4..8140811 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/TickerView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/TickerView.java
@@ -34,5 +34,9 @@
super.onSizeChanged(w, h, oldw, oldh);
mTicker.reflowText();
}
+
+ public void setTicker(Ticker t) {
+ mTicker = t;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java
index 3c7b130..7c7d74c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java
@@ -31,24 +31,16 @@
public class NotificationIconArea extends LinearLayout {
private static final String TAG = "NotificationIconArea";
- MoreView mMoreView;
IconLayout mIconLayout;
DraggerView mDraggerView;
public NotificationIconArea(Context context, AttributeSet attrs) {
super(context, attrs);
- mMoreView = (MoreView) findViewById(R.id.more);
mIconLayout = (IconLayout)findViewById(R.id.icons);
mDraggerView = (DraggerView) findViewById(R.id.handle);
}
- static class MoreView extends ImageView {
- public MoreView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
- }
-
static class IconLayout extends LinearLayout {
public IconLayout(Context context, AttributeSet attrs) {
super(context, attrs);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
index b1c4ee3..9946ada 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
@@ -16,30 +16,34 @@
package com.android.systemui.statusbar.tablet;
+import android.animation.Animator;
+import android.app.ActivityManagerNative;
import android.app.Notification;
+import android.app.PendingIntent;
import android.app.Service;
+import android.app.StatusBarManager;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
+import android.os.RemoteException;
import android.util.Slog;
import android.view.Gravity;
import android.view.LayoutInflater;
-import android.widget.RemoteViews;
-import android.app.ActivityManagerNative;
-import android.app.PendingIntent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
-import android.graphics.Rect;
-import android.os.RemoteException;
import android.view.WindowManagerImpl;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import android.widget.RemoteViews;
+import android.widget.ScrollView;
+import android.widget.TextSwitcher;
import android.widget.TextView;
import com.android.internal.statusbar.StatusBarIcon;
@@ -53,13 +57,18 @@
public static final boolean DEBUG = false;
public static final String TAG = "TabletStatusBar";
- View mStatusBarView;
- NotificationIconArea mNotificationIconArea;
+
int mIconSize;
H mHandler = new H();
+ // tracking all current notifications
+ private NotificationData mNotns = new NotificationData();
+
+ View mStatusBarView;
+ NotificationIconArea mNotificationIconArea;
+
View mNotificationPanel;
View mSystemPanel;
@@ -68,8 +77,14 @@
NotificationIconArea.IconLayout mIconLayout;
- private NotificationData mNotns = new NotificationData();
-
+ KickerController mKicker;
+ View mKickerView;
+ boolean mTicking;
+ boolean mExpandedVisible;
+
+ // for disabling the status bar
+ int mDisabled = 0;
+
protected void addPanelWindows() {
mNotificationPanel = View.inflate(this, R.layout.sysbar_panel_notifications, null);
mSystemPanel = View.inflate(this, R.layout.sysbar_panel_system, null);
@@ -86,8 +101,8 @@
ViewGroup.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
PixelFormat.TRANSLUCENT);
@@ -102,8 +117,8 @@
200, // ViewGroup.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
PixelFormat.TRANSLUCENT);
@@ -112,23 +127,11 @@
lp.windowAnimations = com.android.internal.R.style.Animation_SlidingCard;
WindowManagerImpl.getDefault().addView(mSystemPanel, lp);
-
- // Lorem ipsum, Dolores
- TextView tv = ((TextView) mSystemPanel.findViewById(R.id.systemPanelDummy));
- if (tv != null) tv.setText("System status: great");
-
- mPile = (ViewGroup)mNotificationPanel.findViewById(R.id.content);
- mPile.removeAllViews();
-
- mClearButton = (TextView)mNotificationPanel.findViewById(R.id.clear_all_button);
- mClearButton.setOnClickListener(mClearButtonListener);
}
@Override
public void onCreate() {
super.onCreate(); // will add the main bar view
-
- addPanelWindows();
}
protected View makeStatusBarView() {
@@ -145,6 +148,24 @@
// where the icons go
mIconLayout = (NotificationIconArea.IconLayout) sb.findViewById(R.id.icons);
+ mKicker = new KickerController((Context)this, mStatusBarView);
+
+ // Add the windows
+ addPanelWindows();
+
+ // Lorem ipsum, Dolores
+ TextView tv = ((TextView) mSystemPanel.findViewById(R.id.systemPanelDummy));
+ if (tv != null) tv.setText("System status: great");
+
+ mPile = (ViewGroup)mNotificationPanel.findViewById(R.id.content);
+ mPile.removeAllViews();
+
+ ScrollView scroller = (ScrollView)mPile.getParent();
+ scroller.setFillViewport(true);
+
+ mClearButton = (TextView)mNotificationPanel.findViewById(R.id.clear_all_button);
+ mClearButton.setOnClickListener(mClearButtonListener);
+
return sb;
}
@@ -162,10 +183,12 @@
case MSG_OPEN_NOTIFICATION_PANEL:
if (DEBUG) Slog.d(TAG, "opening notifications panel");
mNotificationPanel.setVisibility(View.VISIBLE);
+ mExpandedVisible = true;
break;
case MSG_CLOSE_NOTIFICATION_PANEL:
if (DEBUG) Slog.d(TAG, "closing notifications panel");
mNotificationPanel.setVisibility(View.GONE);
+ mExpandedVisible = false;
break;
case MSG_OPEN_SYSTEM_PANEL:
if (DEBUG) Slog.d(TAG, "opening system panel");
@@ -195,8 +218,7 @@
public void addNotification(IBinder key, StatusBarNotification notification) {
if (DEBUG) Slog.d(TAG, "addNotification(" + key + " -> " + notification + ")");
addNotificationViews(key, notification);
-
- // TODO: kicker; immersive mode
+ // tick()
}
public void updateNotification(IBinder key, StatusBarNotification notification) {
@@ -274,7 +296,127 @@
}
public void disable(int state) {
- // TODO
+ /*
+ final int old = mDisabled;
+ final int diff = state ^ old;
+ mDisabled = state;
+
+ if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
+ if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
+ Slog.d(TAG, "DISABLE_EXPAND: yes");
+ animateCollapse();
+ }
+ }
+ if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+ if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
+ if (mTicking) {
+ mKicker.halt();
+ } else {
+ mNotificationIconArea.setVisibility(View.INVISIBLE);
+ }
+ } else {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
+ if (!mExpandedVisible) {
+ mNotificationIconArea.setVisibility(View.VISIBLE);
+ }
+ }
+ } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: yes");
+ mKicker.halt();
+ }
+ }
+ */
+ }
+
+ void performDisableActions(int net) {
+ /*
+ int old = mDisabled;
+ int diff = net ^ old;
+ mDisabled = net;
+
+ // act accordingly
+ if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
+ if ((net & StatusBarManager.DISABLE_EXPAND) != 0) {
+ Slog.d(TAG, "DISABLE_EXPAND: yes");
+ animateCollapse();
+ }
+ }
+ if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+ if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
+ if (mTicking) {
+ mNotificationIconArea.setVisibility(View.INVISIBLE);
+ mKicker.halt();
+ } else {
+ mNotificationIconArea.setVisibility(View.INVISIBLE);
+ }
+ } else {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
+ if (!mExpandedVisible) {
+ mNotificationIconArea.setVisibility(View.VISIBLE);
+ }
+ }
+ } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ mKicker.halt();
+ }
+ }
+ */
+ }
+
+ private void tick(StatusBarNotification n) {
+ // Show the ticker if one is requested. Also don't do this
+ // until status bar window is attached to the window manager,
+ // because... well, what's the point otherwise? And trying to
+ // run a ticker without being attached will crash!
+ if (n.notification.tickerText != null && mStatusBarView.getWindowToken() != null) {
+ if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
+ | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
+ mKicker.addEntry(n);
+ }
+ }
+ }
+
+ private class KickerController {
+ View mView;
+ ImageView mKickerIcon;
+ TextSwitcher mKickerText;
+
+ public KickerController(Context context, View sb) {
+ mView = sb.findViewById(R.id.ticker);
+ mKickerIcon = (ImageView) mView.findViewById(R.id.tickerIcon);
+ mKickerText = (TextSwitcher) mView.findViewById(R.id.tickerText);
+ }
+
+ public void halt() {
+ tickerHalting();
+ }
+
+ public void addEntry(StatusBarNotification n) {
+ mKickerIcon.setImageResource(n.notification.icon);
+ mKickerText.setCurrentText(n.notification.tickerText);
+ tickerStarting();
+ }
+
+ public void tickerStarting() {
+ mTicking = true;
+ mIconLayout.setVisibility(View.GONE);
+ mKickerView.setVisibility(View.VISIBLE);
+ }
+
+ public void tickerDone() {
+ mIconLayout.setVisibility(View.VISIBLE);
+ mKickerView.setVisibility(View.GONE);
+ mTicking = false;
+ }
+
+ public void tickerHalting() {
+ mIconLayout.setVisibility(View.VISIBLE);
+ mKickerView.setVisibility(View.GONE);
+ mTicking = false;
+ }
}
public void animateExpand() {
@@ -405,8 +547,9 @@
}
StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) {
- NotificationData list = mNotns;
- ViewGroup parent = mPile;
+ if (DEBUG) {
+ Slog.d(TAG, "addNotificationViews(key=" + key + ", notification=" + notification);
+ }
// Construct the icon.
final StatusBarIconView iconView = new StatusBarIconView(this,
notification.pkg + "/0x" + Integer.toHexString(notification.id));
@@ -422,19 +565,13 @@
}
// Construct the expanded view.
NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView);
- if (!inflateViews(entry, parent)) {
+ if (!inflateViews(entry, mPile)) {
handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
+ notification);
return null;
}
- // Add the expanded view.
- final int viewIndex = list.add(entry);
- if (parent != null) parent.addView(entry.row, viewIndex);
// Add the icon.
-// final int iconIndex = 0; // XXX: sort into ongoing and regular buckets
-// mIconLayout.addView(iconView, iconIndex,
-// new LinearLayout.LayoutParams(mIconSize, mIconSize));
-
+ mNotns.add(entry);
refreshIcons();
return iconView;
@@ -443,13 +580,25 @@
private void refreshIcons() {
// XXX: need to implement a new limited linear layout class
// to avoid removing & readding everything
- mIconLayout.removeAllViews();
+
int N = mNotns.size();
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(mIconSize, mIconSize);
+
+ if (DEBUG) {
+ Slog.d(TAG, "refreshing icons (" + N + " notifications, mIconLayout="
+ + mIconLayout + ", mPile=" + mPile);
+ }
+
+ mIconLayout.removeAllViews();
for (int i=0; i<4; i++) {
if (i>=N) break;
mIconLayout.addView(mNotns.get(N-i-1).icon, i, params);
}
+
+ mPile.removeAllViews();
+ for (int i=0; i<N; i++) {
+ mPile.addView(mNotns.get(N-i-1).row);
+ }
}
private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 73fa93c..8a6428b 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -406,7 +406,12 @@
@Override
public final void openPanel(int featureId, KeyEvent event) {
- openPanel(getPanelState(featureId, true), event);
+ if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null &&
+ mActionBar.isOverflowReserved()) {
+ mActionBar.showOverflowMenu();
+ } else {
+ openPanel(getPanelState(featureId, true), event);
+ }
}
private void openPanel(PanelFeatureState st, KeyEvent event) {
@@ -497,7 +502,10 @@
@Override
public final void closePanel(int featureId) {
- if (featureId == FEATURE_CONTEXT_MENU) {
+ if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null &&
+ mActionBar.isOverflowReserved()) {
+ mActionBar.hideOverflowMenu();
+ } else if (featureId == FEATURE_CONTEXT_MENU) {
closeContextMenu();
} else {
closePanel(getPanelState(featureId, true), true);
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 252b42a..1c7faa4 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1696,7 +1696,10 @@
// Delegate master volume control to effect in output mix effect chain if needed
sp<EffectChain> chain = getEffectChain_l(AudioSystem::SESSION_OUTPUT_MIX);
if (chain != 0) {
- uint32_t v = (uint32_t)(masterVolume * (1 << 24));
+ uint32_t v = 0;
+ if (!masterMute) {
+ v = (uint32_t)(masterVolume * (1 << 24));
+ }
chain->setVolume_l(&v, &v);
masterVolume = (float)((v + (1 << 23)) >> 24);
chain.clear();
@@ -1750,7 +1753,7 @@
// compute volume for this track
int16_t left, right, aux;
- if (track->isMuted() || masterMute || track->isPausing() ||
+ if (track->isMuted() || track->isPausing() ||
mStreamTypes[track->type()].mute) {
left = right = aux = 0;
if (track->isPausing()) {
@@ -5351,7 +5354,7 @@
return;
}
- if (mState == ACTIVE || mState == STOPPING || mState == STOPPED) {
+ if (mState == ACTIVE || mState == STOPPING || mState == STOPPED || mState == RESTART) {
// do 32 bit to 16 bit conversion for auxiliary effect input buffer
if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
AudioMixer::ditherAndClamp(mConfig.inputCfg.buffer.s32,
@@ -6032,8 +6035,8 @@
AudioFlinger::EffectChain::EffectChain(const wp<ThreadBase>& wThread,
int sessionId)
: mThread(wThread), mSessionId(sessionId), mActiveTrackCnt(0), mOwnInBuffer(false),
- mVolumeCtrlIdx(-1), mLeftVolume(0), mRightVolume(0),
- mNewLeftVolume(0), mNewRightVolume(0)
+ mVolumeCtrlIdx(-1), mLeftVolume(UINT_MAX), mRightVolume(UINT_MAX),
+ mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX)
{
mStrategy = AudioSystem::getStrategyForStream(AudioSystem::MUSIC);
}
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index cc1566b..ae4e168 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -314,14 +314,14 @@
case ConnectivityManager.TYPE_WIFI:
if (DBG) Slog.v(TAG, "Starting Wifi Service.");
WifiStateTracker wst = new WifiStateTracker(context, mHandler);
- WifiService wifiService = new WifiService(context, wst);
+ WifiService wifiService = new WifiService(context);
ServiceManager.addService(Context.WIFI_SERVICE, wifiService);
- wifiService.startWifi();
+ wifiService.checkAndStartWifi();
mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst;
wst.startMonitoring();
//TODO: as part of WWS refactor, create only when needed
- mWifiWatchdogService = new WifiWatchdogService(context, wst);
+ mWifiWatchdogService = new WifiWatchdogService(context);
break;
case ConnectivityManager.TYPE_MOBILE:
@@ -1205,16 +1205,6 @@
sendConnectedBroadcast(info);
}
- private void handleScanResultsAvailable(NetworkInfo info) {
- int networkType = info.getType();
- if (networkType != ConnectivityManager.TYPE_WIFI) {
- if (DBG) Slog.v(TAG, "Got ScanResultsAvailable for " +
- info.getTypeName() + " network. Don't know how to handle.");
- }
-
- mNetTrackers[networkType].interpretScanResultsAvailable();
- }
-
private void handleNotificationChange(boolean visible, int id,
Notification notification) {
NotificationManager notificationManager = (NotificationManager) mContext
@@ -1619,11 +1609,6 @@
}
break;
- case NetworkStateTracker.EVENT_SCAN_RESULTS_AVAILABLE:
- info = (NetworkInfo) msg.obj;
- handleScanResultsAvailable(info);
- break;
-
case NetworkStateTracker.EVENT_NOTIFICATION_CHANGED:
handleNotificationChange(msg.arg1 == 1, msg.arg2,
(Notification) msg.obj);
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 36b3a5e..3fd9907 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -77,9 +77,12 @@
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
+import java.text.Collator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
/**
* This class provides a system service that manages input methods.
@@ -1508,21 +1511,22 @@
hideInputMethodMenuLocked();
int N = immis.size();
-
- mItems = new CharSequence[N];
- mIms = new InputMethodInfo[N];
-
- int j = 0;
+
+ final Map<CharSequence, InputMethodInfo> imMap =
+ new TreeMap<CharSequence, InputMethodInfo>(Collator.getInstance());
+
for (int i = 0; i < N; ++i) {
InputMethodInfo property = immis.get(i);
if (property == null) {
continue;
}
- mItems[j] = property.loadLabel(pm);
- mIms[j] = property;
- j++;
+ imMap.put(property.loadLabel(pm), property);
}
-
+
+ N = imMap.size();
+ mItems = imMap.keySet().toArray(new CharSequence[N]);
+ mIms = imMap.values().toArray(new InputMethodInfo[N]);
+
int checkedItem = 0;
for (int i = 0; i < N; ++i) {
if (mIms[i].getId().equals(lastInputMethodId)) {
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 15080b2..b43b33e 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -17,6 +17,8 @@
package com.android.server;
import android.app.AlarmManager;
+import android.app.Notification;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothDevice;
@@ -26,25 +28,30 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.database.ContentObserver;
import android.net.wifi.IWifiManager;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.net.wifi.WifiStateTracker;
+import android.net.wifi.WifiStateMachine;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.ConnectivityManager;
import android.net.InterfaceConfiguration;
-import android.net.NetworkStateTracker;
import android.net.DhcpInfo;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.State;
import android.os.Binder;
+import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.INetworkManagementService;
+import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.Slog;
import java.util.ArrayList;
@@ -72,7 +79,7 @@
private static final String TAG = "WifiService";
private static final boolean DBG = true;
- private final WifiStateTracker mWifiStateTracker;
+ private final WifiStateMachine mWifiStateMachine;
private Context mContext;
@@ -123,10 +130,63 @@
private boolean mIsReceiverRegistered = false;
- WifiService(Context context, WifiStateTracker tracker) {
+
+ NetworkInfo mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, "WIFI", "");
+
+ // Variables relating to the 'available networks' notification
+ /**
+ * The icon to show in the 'available networks' notification. This will also
+ * be the ID of the Notification given to the NotificationManager.
+ */
+ private static final int ICON_NETWORKS_AVAILABLE =
+ com.android.internal.R.drawable.stat_notify_wifi_in_range;
+ /**
+ * When a notification is shown, we wait this amount before possibly showing it again.
+ */
+ private final long NOTIFICATION_REPEAT_DELAY_MS;
+ /**
+ * Whether the user has set the setting to show the 'available networks' notification.
+ */
+ private boolean mNotificationEnabled;
+ /**
+ * Observes the user setting to keep {@link #mNotificationEnabled} in sync.
+ */
+ private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver;
+ /**
+ * The {@link System#currentTimeMillis()} must be at least this value for us
+ * to show the notification again.
+ */
+ private long mNotificationRepeatTime;
+ /**
+ * The Notification object given to the NotificationManager.
+ */
+ private Notification mNotification;
+ /**
+ * Whether the notification is being shown, as set by us. That is, if the
+ * user cancels the notification, we will not receive the callback so this
+ * will still be true. We only guarantee if this is false, then the
+ * notification is not showing.
+ */
+ private boolean mNotificationShown;
+ /**
+ * The number of continuous scans that must occur before consider the
+ * supplicant in a scanning state. This allows supplicant to associate with
+ * remembered networks that are in the scan results.
+ */
+ private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3;
+ /**
+ * The number of scans since the last network state change. When this
+ * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the
+ * supplicant to actually be scanning. When the network state changes to
+ * something other than scanning, we reset this to 0.
+ */
+ private int mNumScansSinceNetworkStateChange;
+
+
+ WifiService(Context context) {
mContext = context;
- mWifiStateTracker = tracker;
- mWifiStateTracker.enableRssiPolling(true);
+ mWifiStateMachine = new WifiStateMachine(mContext);
+ mWifiStateMachine.enableRssiPolling(true);
mBatteryStats = BatteryStatsService.getService();
mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
@@ -164,6 +224,42 @@
}
},new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+
+ mContext.registerReceiver(
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
+ // reset & clear notification on any wifi state change
+ resetNotification();
+ } else if (intent.getAction().equals(
+ WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+ mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
+ WifiManager.EXTRA_NETWORK_INFO);
+ // reset & clear notification on a network connect & disconnect
+ switch(mNetworkInfo.getDetailedState()) {
+ case CONNECTED:
+ case DISCONNECTED:
+ resetNotification();
+ break;
+ }
+ } else if (intent.getAction().equals(
+ WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+ checkAndSetNotification();
+ }
+ }
+ }, filter);
+
+ // Setting is in seconds
+ NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l;
+ mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(new Handler());
+ mNotificationEnabledSettingObserver.register();
}
/**
@@ -172,7 +268,7 @@
*
* This function is used only at boot time
*/
- public void startWifi() {
+ public void checkAndStartWifi() {
/* Start if Wi-Fi is enabled or the saved state indicates Wi-Fi was on */
boolean wifiEnabled = !isAirplaneModeOn()
&& (getPersistedWifiEnabled() || testAndClearWifiSavedState());
@@ -255,17 +351,13 @@
Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, enabled ? 1 : 0);
}
- NetworkStateTracker getNetworkStateTracker() {
- return mWifiStateTracker;
- }
-
/**
* see {@link android.net.wifi.WifiManager#pingSupplicant()}
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public boolean pingSupplicant() {
enforceAccessPermission();
- return mWifiStateTracker.pingSupplicant();
+ return mWifiStateMachine.pingSupplicant();
}
/**
@@ -274,7 +366,7 @@
*/
public boolean startScan(boolean forceActive) {
enforceChangePermission();
- return mWifiStateTracker.startScan(forceActive);
+ return mWifiStateMachine.startScan(forceActive);
}
private void enforceAccessPermission() {
@@ -304,7 +396,7 @@
enforceChangePermission();
if (DBG) {
- Slog.e(TAG, "Invoking mWifiStateTracker.setWifiEnabled\n");
+ Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n");
}
// set a flag if the user is enabling Wifi while in airplane mode
@@ -312,7 +404,7 @@
mAirplaneModeOverwridden.set(true);
}
- mWifiStateTracker.setWifiEnabled(enable);
+ mWifiStateMachine.setWifiEnabled(enable);
persistWifiEnabled(enable);
if (enable) {
@@ -338,7 +430,7 @@
*/
public int getWifiEnabledState() {
enforceAccessPermission();
- return mWifiStateTracker.getWifiState();
+ return mWifiStateMachine.getWifiState();
}
/**
@@ -362,7 +454,7 @@
setWifiApConfiguration(wifiConfig);
}
- mWifiStateTracker.setWifiApEnabled(wifiConfig, enabled);
+ mWifiStateMachine.setWifiApEnabled(wifiConfig, enabled);
return true;
}
@@ -377,7 +469,7 @@
*/
public int getWifiApEnabledState() {
enforceAccessPermission();
- return mWifiStateTracker.getWifiApState();
+ return mWifiStateMachine.getWifiApState();
}
/**
@@ -427,7 +519,7 @@
*/
public boolean disconnect() {
enforceChangePermission();
- return mWifiStateTracker.disconnectCommand();
+ return mWifiStateMachine.disconnectCommand();
}
/**
@@ -436,7 +528,7 @@
*/
public boolean reconnect() {
enforceChangePermission();
- return mWifiStateTracker.reconnectCommand();
+ return mWifiStateMachine.reconnectCommand();
}
/**
@@ -445,7 +537,7 @@
*/
public boolean reassociate() {
enforceChangePermission();
- return mWifiStateTracker.reassociateCommand();
+ return mWifiStateMachine.reassociateCommand();
}
/**
@@ -454,7 +546,7 @@
*/
public List<WifiConfiguration> getConfiguredNetworks() {
enforceAccessPermission();
- return mWifiStateTracker.getConfiguredNetworks();
+ return mWifiStateMachine.getConfiguredNetworks();
}
/**
@@ -464,7 +556,7 @@
*/
public int addOrUpdateNetwork(WifiConfiguration config) {
enforceChangePermission();
- return mWifiStateTracker.addOrUpdateNetwork(config);
+ return mWifiStateMachine.addOrUpdateNetwork(config);
}
/**
@@ -475,7 +567,7 @@
*/
public boolean removeNetwork(int netId) {
enforceChangePermission();
- return mWifiStateTracker.removeNetwork(netId);
+ return mWifiStateMachine.removeNetwork(netId);
}
/**
@@ -487,7 +579,7 @@
*/
public boolean enableNetwork(int netId, boolean disableOthers) {
enforceChangePermission();
- return mWifiStateTracker.enableNetwork(netId, disableOthers);
+ return mWifiStateMachine.enableNetwork(netId, disableOthers);
}
/**
@@ -498,7 +590,7 @@
*/
public boolean disableNetwork(int netId) {
enforceChangePermission();
- return mWifiStateTracker.disableNetwork(netId);
+ return mWifiStateMachine.disableNetwork(netId);
}
/**
@@ -511,7 +603,7 @@
* Make sure we have the latest information, by sending
* a status request to the supplicant.
*/
- return mWifiStateTracker.requestConnectionInfo();
+ return mWifiStateMachine.requestConnectionInfo();
}
/**
@@ -521,7 +613,7 @@
*/
public List<ScanResult> getScanResults() {
enforceAccessPermission();
- return mWifiStateTracker.getScanResultsList();
+ return mWifiStateMachine.getScanResultsList();
}
/**
@@ -533,7 +625,7 @@
public boolean saveConfiguration() {
boolean result = true;
enforceChangePermission();
- return mWifiStateTracker.saveConfig();
+ return mWifiStateMachine.saveConfig();
}
/**
@@ -576,7 +668,7 @@
numChannels);
}
- mWifiStateTracker.setNumAllowedChannels(numChannels);
+ mWifiStateMachine.setNumAllowedChannels(numChannels);
return true;
}
@@ -596,7 +688,7 @@
* Wi-Fi is not currently enabled), get the value from
* Settings.
*/
- numChannels = mWifiStateTracker.getNumAllowedChannels();
+ numChannels = mWifiStateMachine.getNumAllowedChannels();
if (numChannels < 0) {
numChannels = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS,
@@ -622,9 +714,59 @@
*/
public DhcpInfo getDhcpInfo() {
enforceAccessPermission();
- return mWifiStateTracker.getDhcpInfo();
+ return mWifiStateMachine.getDhcpInfo();
}
+ /**
+ * see {@link android.net.wifi.WifiManager#startWifi}
+ *
+ */
+ public void startWifi() {
+ enforceChangePermission();
+ /* TODO: may be add permissions for access only to connectivity service
+ * TODO: if a start issued, keep wifi alive until a stop issued irrespective
+ * of WifiLock & device idle status unless wifi enabled status is toggled
+ */
+
+ mWifiStateMachine.setDriverStart(true);
+ mWifiStateMachine.reconnectCommand();
+ }
+
+ /**
+ * see {@link android.net.wifi.WifiManager#stopWifi}
+ *
+ */
+ public void stopWifi() {
+ enforceChangePermission();
+ /* TODO: may be add permissions for access only to connectivity service
+ * TODO: if a stop is issued, wifi is brought up only by startWifi
+ * unless wifi enabled status is toggled
+ */
+ mWifiStateMachine.setDriverStart(false);
+ }
+
+
+ /**
+ * see {@link android.net.wifi.WifiManager#addToBlacklist}
+ *
+ */
+ public void addToBlacklist(String bssid) {
+ enforceChangePermission();
+
+ mWifiStateMachine.addToBlacklist(bssid);
+ }
+
+ /**
+ * see {@link android.net.wifi.WifiManager#clearBlacklist}
+ *
+ */
+ public void clearBlacklist() {
+ enforceChangePermission();
+
+ mWifiStateMachine.clearBlacklist();
+ }
+
+
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -641,11 +783,11 @@
mAlarmManager.cancel(mIdleIntent);
mDeviceIdle = false;
mScreenOff = false;
- mWifiStateTracker.enableRssiPolling(true);
+ mWifiStateMachine.enableRssiPolling(true);
} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
Slog.d(TAG, "ACTION_SCREEN_OFF");
mScreenOff = true;
- mWifiStateTracker.enableRssiPolling(false);
+ mWifiStateMachine.enableRssiPolling(false);
/*
* Set a timer to put Wi-Fi to sleep, but only if the screen is off
* AND the "stay on while plugged in" setting doesn't match the
@@ -653,7 +795,7 @@
* or plugged in to AC).
*/
if (!shouldWifiStayAwake(stayAwakeConditions, mPluggedType)) {
- WifiInfo info = mWifiStateTracker.requestConnectionInfo();
+ WifiInfo info = mWifiStateMachine.requestConnectionInfo();
if (info.getSupplicantState() != SupplicantState.COMPLETED) {
// we used to go to sleep immediately, but this caused some race conditions
// we don't have time to track down for this release. Delay instead,
@@ -704,7 +846,7 @@
isBluetoothPlaying = true;
}
}
- mWifiStateTracker.setBluetoothScanMode(isBluetoothPlaying);
+ mWifiStateMachine.setBluetoothScanMode(isBluetoothPlaying);
} else {
return;
@@ -771,21 +913,21 @@
/* Disable tethering when airplane mode is enabled */
if (airplaneMode) {
- mWifiStateTracker.setWifiApEnabled(null, false);
+ mWifiStateMachine.setWifiApEnabled(null, false);
}
if (wifiShouldBeEnabled) {
if (wifiShouldBeStarted) {
- mWifiStateTracker.setWifiEnabled(true);
- mWifiStateTracker.setScanOnlyMode(
+ mWifiStateMachine.setWifiEnabled(true);
+ mWifiStateMachine.setScanOnlyMode(
strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY);
- mWifiStateTracker.startWifi(true);
+ mWifiStateMachine.setDriverStart(true);
} else {
- mWifiStateTracker.requestCmWakeLock();
- mWifiStateTracker.disconnectAndStop();
+ mWifiStateMachine.requestCmWakeLock();
+ mWifiStateMachine.setDriverStart(false);
}
} else {
- mWifiStateTracker.setWifiEnabled(false);
+ mWifiStateMachine.setWifiEnabled(false);
}
}
@@ -832,17 +974,17 @@
+ ", uid=" + Binder.getCallingUid());
return;
}
- pw.println("Wi-Fi is " + mWifiStateTracker.getWifiStateByName());
+ pw.println("Wi-Fi is " + mWifiStateMachine.getWifiStateByName());
pw.println("Stay-awake conditions: " +
Settings.System.getInt(mContext.getContentResolver(),
Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0));
pw.println();
pw.println("Internal state:");
- pw.println(mWifiStateTracker);
+ pw.println(mWifiStateMachine);
pw.println();
pw.println("Latest scan results:");
- List<ScanResult> scanResults = mWifiStateTracker.getScanResultsList();
+ List<ScanResult> scanResults = mWifiStateMachine.getScanResultsList();
if (scanResults != null && scanResults.size() != 0) {
pw.println(" BSSID Frequency RSSI Flags SSID");
for (ScanResult r : scanResults) {
@@ -1069,7 +1211,7 @@
if (mMulticasters.size() != 0) {
return;
} else {
- mWifiStateTracker.startPacketFiltering();
+ mWifiStateMachine.startPacketFiltering();
}
}
}
@@ -1084,7 +1226,7 @@
// our new size == 1 (first call), but this function won't
// be called often and by making the stopPacket call each
// time we're less fragile and self-healing.
- mWifiStateTracker.stopPacketFiltering();
+ mWifiStateMachine.stopPacketFiltering();
}
int uid = Binder.getCallingUid();
@@ -1121,7 +1263,7 @@
removed.unlinkDeathRecipient();
}
if (mMulticasters.size() == 0) {
- mWifiStateTracker.startPacketFiltering();
+ mWifiStateMachine.startPacketFiltering();
}
Long ident = Binder.clearCallingIdentity();
@@ -1140,4 +1282,166 @@
return (mMulticasters.size() > 0);
}
}
+
+ private void checkAndSetNotification() {
+ // If we shouldn't place a notification on available networks, then
+ // don't bother doing any of the following
+ if (!mNotificationEnabled) return;
+
+ State state = mNetworkInfo.getState();
+ if ((state == NetworkInfo.State.DISCONNECTED)
+ || (state == NetworkInfo.State.UNKNOWN)) {
+ // Look for an open network
+ List<ScanResult> scanResults = mWifiStateMachine.getScanResultsList();
+ if (scanResults != null) {
+ int numOpenNetworks = 0;
+ for (int i = scanResults.size() - 1; i >= 0; i--) {
+ ScanResult scanResult = scanResults.get(i);
+
+ if (TextUtils.isEmpty(scanResult.capabilities)) {
+ numOpenNetworks++;
+ }
+ }
+
+ if (numOpenNetworks > 0) {
+ if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) {
+ /*
+ * We've scanned continuously at least
+ * NUM_SCANS_BEFORE_NOTIFICATION times. The user
+ * probably does not have a remembered network in range,
+ * since otherwise supplicant would have tried to
+ * associate and thus resetting this counter.
+ */
+ setNotificationVisible(true, numOpenNetworks, false, 0);
+ }
+ return;
+ }
+ }
+ }
+
+ // No open networks in range, remove the notification
+ setNotificationVisible(false, 0, false, 0);
+ }
+
+ /**
+ * Clears variables related to tracking whether a notification has been
+ * shown recently and clears the current notification.
+ */
+ private void resetNotification() {
+ mNotificationRepeatTime = 0;
+ mNumScansSinceNetworkStateChange = 0;
+ setNotificationVisible(false, 0, false, 0);
+ }
+
+ /**
+ * Display or don't display a notification that there are open Wi-Fi networks.
+ * @param visible {@code true} if notification should be visible, {@code false} otherwise
+ * @param numNetworks the number networks seen
+ * @param force {@code true} to force notification to be shown/not-shown,
+ * even if it is already shown/not-shown.
+ * @param delay time in milliseconds after which the notification should be made
+ * visible or invisible.
+ */
+ private void setNotificationVisible(boolean visible, int numNetworks, boolean force,
+ int delay) {
+
+ // Since we use auto cancel on the notification, when the
+ // mNetworksAvailableNotificationShown is true, the notification may
+ // have actually been canceled. However, when it is false we know
+ // for sure that it is not being shown (it will not be shown any other
+ // place than here)
+
+ // If it should be hidden and it is already hidden, then noop
+ if (!visible && !mNotificationShown && !force) {
+ return;
+ }
+
+ NotificationManager notificationManager = (NotificationManager) mContext
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+
+ Message message;
+ if (visible) {
+
+ // Not enough time has passed to show the notification again
+ if (System.currentTimeMillis() < mNotificationRepeatTime) {
+ return;
+ }
+
+ if (mNotification == null) {
+ // Cache the Notification mainly so we can remove the
+ // EVENT_NOTIFICATION_CHANGED message with this Notification from
+ // the queue later
+ mNotification = new Notification();
+ mNotification.when = 0;
+ mNotification.icon = ICON_NETWORKS_AVAILABLE;
+ mNotification.flags = Notification.FLAG_AUTO_CANCEL;
+ mNotification.contentIntent = PendingIntent.getActivity(mContext, 0,
+ new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK), 0);
+ }
+
+ CharSequence title = mContext.getResources().getQuantityText(
+ com.android.internal.R.plurals.wifi_available, numNetworks);
+ CharSequence details = mContext.getResources().getQuantityText(
+ com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
+ mNotification.tickerText = title;
+ mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent);
+
+ mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS;
+
+ notificationManager.notify(ICON_NETWORKS_AVAILABLE, mNotification);
+ /*
+ * TODO: Clean up connectivity service & remove this
+ */
+ /* message = mCsHandler.obtainMessage(EVENT_NOTIFICATION_CHANGED, 1,
+ ICON_NETWORKS_AVAILABLE, mNotification); */
+
+
+ } else {
+
+ notificationManager.cancel(ICON_NETWORKS_AVAILABLE);
+ /*
+ * TODO: Clean up connectivity service & remove this
+ */
+ /*
+ // Remove any pending messages to show the notification
+ mCsHandler.removeMessages(EVENT_NOTIFICATION_CHANGED, mNotification);
+
+ message = mCsHandler.obtainMessage(EVENT_NOTIFICATION_CHANGED, 0,
+ ICON_NETWORKS_AVAILABLE);
+ */
+ }
+
+ //mCsHandler.sendMessageDelayed(message, delay);
+
+ mNotificationShown = visible;
+ }
+
+ private class NotificationEnabledSettingObserver extends ContentObserver {
+
+ public NotificationEnabledSettingObserver(Handler handler) {
+ super(handler);
+ }
+
+ public void register() {
+ ContentResolver cr = mContext.getContentResolver();
+ cr.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
+ mNotificationEnabled = getValue();
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+
+ mNotificationEnabled = getValue();
+ resetNotification();
+ }
+
+ private boolean getValue() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
+ }
+ }
+
+
}
diff --git a/services/java/com/android/server/WifiWatchdogService.java b/services/java/com/android/server/WifiWatchdogService.java
index be14cd3..46d6bef 100644
--- a/services/java/com/android/server/WifiWatchdogService.java
+++ b/services/java/com/android/server/WifiWatchdogService.java
@@ -27,7 +27,6 @@
import android.net.wifi.ScanResult;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.net.wifi.WifiStateTracker;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -77,7 +76,6 @@
private Context mContext;
private ContentResolver mContentResolver;
- private WifiStateTracker mWifiStateTracker;
private WifiManager mWifiManager;
/**
@@ -108,10 +106,9 @@
/** Whether the current AP check should be canceled. */
private boolean mShouldCancel;
- WifiWatchdogService(Context context, WifiStateTracker wifiStateTracker) {
+ WifiWatchdogService(Context context) {
mContext = context;
mContentResolver = context.getContentResolver();
- mWifiStateTracker = wifiStateTracker;
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
createThread();
@@ -752,7 +749,7 @@
// Black list this "bad" AP, this will cause an attempt to connect to another
blacklistAp(ap.bssid);
// Initiate an association to an alternate AP
- mWifiStateTracker.reassociateCommand();
+ mWifiManager.reassociate();
}
private void blacklistAp(String bssid) {
@@ -763,7 +760,7 @@
// Before taking action, make sure we should not cancel our processing
if (shouldCancel()) return;
- mWifiStateTracker.addToBlacklist(bssid);
+ mWifiManager.addToBlacklist(bssid);
if (D) {
myLogD("Blacklisting " + bssid);
@@ -858,7 +855,7 @@
* (and blacklisted them). Clear the blacklist so the AP with best
* signal is chosen.
*/
- mWifiStateTracker.clearBlacklist();
+ mWifiManager.clearBlacklist();
if (V) {
myLogV("handleSleep: Set state to SLEEP and cleared blacklist");
@@ -929,7 +926,7 @@
* should revert anything done by the watchdog monitoring.
*/
private void handleReset() {
- mWifiStateTracker.clearBlacklist();
+ mWifiManager.clearBlacklist();
setIdleState(true);
}
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index b1e8968..e4ef09c 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -7862,6 +7862,7 @@
} catch (RemoteException e) {
// Ignore if process has died.
}
+ notifyFocusChanged();
}
if (lastFocus != null) {
@@ -7871,7 +7872,6 @@
} catch (RemoteException e) {
// Ignore if process has died.
}
- notifyFocusChanged();
}
}
} break;
diff --git a/telephony/java/com/android/internal/telephony/CallManager.java b/telephony/java/com/android/internal/telephony/CallManager.java
index 1381a2d..2934e6a 100644
--- a/telephony/java/com/android/internal/telephony/CallManager.java
+++ b/telephony/java/com/android/internal/telephony/CallManager.java
@@ -24,6 +24,7 @@
import android.telephony.PhoneStateListener;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -163,6 +164,14 @@
}
/**
+ * Returns all the registered phone objects.
+ * @return all the registered phone objects.
+ */
+ public List<Phone> getAllPhones() {
+ return Collections.unmodifiableList(mPhones);
+ }
+
+ /**
* Get current coarse-grained voice call state.
* If the Call Manager has an active call and call waiting occurs,
* then the phone state is RINGING not OFFHOOK
@@ -206,7 +215,7 @@
* @param phone
*/
public void unregisterPhone(Phone phone) {
- if (phone != null && !mPhones.contains(phone)) {
+ if (phone != null && mPhones.contains(phone)) {
mPhones.remove(phone);
mRingingCalls.remove(phone.getRingingCall());
mBackgroundCalls.remove(phone.getBackgroundCall());
diff --git a/telephony/java/com/android/internal/telephony/PhoneFactory.java b/telephony/java/com/android/internal/telephony/PhoneFactory.java
index 803b736..2e391cb 100644
--- a/telephony/java/com/android/internal/telephony/PhoneFactory.java
+++ b/telephony/java/com/android/internal/telephony/PhoneFactory.java
@@ -24,6 +24,8 @@
import com.android.internal.telephony.cdma.CDMAPhone;
import com.android.internal.telephony.gsm.GSMPhone;
+import com.android.internal.telephony.sip.SipPhone;
+import com.android.internal.telephony.sip.SipPhoneFactory;
/**
* {@hide}
@@ -175,4 +177,13 @@
return phone;
}
}
+
+ /**
+ * Makes a {@link SipPhone} object.
+ * @param sipUri the local SIP URI the phone runs on
+ * @return the {@code SipPhone} object or null if the SIP URI is not valid
+ */
+ public static SipPhone makeSipPhone(String sipUri) {
+ return SipPhoneFactory.makePhone(sipUri, sContext, sPhoneNotifier);
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
index e11bd42..b35814c 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -73,8 +73,6 @@
private static final String LOG_TAG = "SipPhone";
private static final boolean LOCAL_DEBUG = true;
- //private List<SipConnection> connections = new ArrayList<SipConnection>();
-
// A call that is ringing or (call) waiting
private SipCall ringingCall = new SipCall();
private SipCall foregroundCall = new SipCall();
@@ -111,6 +109,10 @@
return mProfile.getProfileName();
}
+ public String getSipUri() {
+ return mProfile.getUriString();
+ }
+
public boolean canTake(Object incomingCall) {
synchronized (SipPhone.class) {
if (!(incomingCall instanceof SipAudioCall)) return false;
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhoneFactory.java b/telephony/java/com/android/internal/telephony/sip/SipPhoneFactory.java
index c9e9762..a1bdd2f 100644
--- a/telephony/java/com/android/internal/telephony/sip/SipPhoneFactory.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhoneFactory.java
@@ -16,7 +16,6 @@
package com.android.internal.telephony.sip;
-import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneNotifier;
import android.content.Context;
@@ -26,42 +25,25 @@
import java.text.ParseException;
/**
- * @hide
+ * {@hide}
*/
public class SipPhoneFactory {
- private static PhoneNotifier sPhoneNotifier = makeDefaultPhoneNotifier();
- private static Context sContext;
-
- public static void makeDefaultPhones(Context context) {
- makeDefaultPhone(context);
- }
-
- public static void makeDefaultPhone(Context context) {
- sContext = context;
- SipPhoneProxy.getInstance().setPhone(
- makePhone("sip:anonymous@localhost"));
- }
-
- public static Phone getDefaultPhone() {
- return SipPhoneProxy.getInstance();
- }
-
- public static SipPhone makePhone(String sipProfileUri) {
+ /**
+ * Makes a {@link SipPhone} object.
+ * @param sipUri the local SIP URI the phone runs on
+ * @param context {@code Context} needed to create a Phone object
+ * @param phoneNotifier {@code PhoneNotifier} needed to create a Phone
+ * object
+ * @return the {@code SipPhone} object or null if the SIP URI is not valid
+ */
+ public static SipPhone makePhone(String sipUri, Context context,
+ PhoneNotifier phoneNotifier) {
try {
- SipProfile profile = new SipProfile.Builder(sipProfileUri).build();
- return new SipPhone(sContext, sPhoneNotifier, profile);
+ SipProfile profile = new SipProfile.Builder(sipUri).build();
+ return new SipPhone(context, phoneNotifier, profile);
} catch (ParseException e) {
- Log.v("SipPhoneProxy", "setPhone", e);
+ Log.w("SipPhoneProxy", "setPhone", e);
return null;
}
}
-
- private static PhoneNotifier makeDefaultPhoneNotifier() {
- try {
- return new com.android.internal.telephony.SipPhoneNotifier();
- } catch (Error e) {
- Log.e("SipPhoneProxy", "makeDefaultPhoneNotifier", e);
- throw e;
- }
- }
}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java b/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
index b62db51..740f544 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
@@ -510,6 +510,14 @@
obtainMessage(SET_GEOLOCATION_PERMISSION, allow ? 1 : 0, 0).sendToTarget();
}
+ public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+ boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+ // Configuration is in WebKit, so stay on WebCore thread, but go via the TestShellActivity
+ // as we need access to the Webview.
+ mLayoutTestController.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
+ canProvideGamma, gamma);
+ }
+
public void overridePreference(String key, boolean value) {
Message message = obtainMessage(OVERRIDE_PREFERENCE);
message.getData().putString("key", key);
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
index acc0ff2..9f580a3 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
@@ -73,7 +73,6 @@
static final String[] ignoreTestList = {
"editing/selection/move-left-right.html", // Causes DumpRenderTree to hang
- "fast/dom/DeviceOrientation/basic-operation.html", // Will cause crash until mock DeviceOrientationClient is hooked up.
"fast/js/excessive-comma-usage.html", // Tests huge initializer list, causes OOM.
"fast/js/regexp-charclass-crash.html", // RegExp is too large, causing OOM
"fast/regex/test1.html", // Causes DumpRenderTree to hang with V8
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
index 15288b5..9be2f1c 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
@@ -71,4 +71,8 @@
// For XSSAuditor tests
public void setXSSAuditorEnabled(boolean flag);
+
+ // For DeviceOrientation tests
+ public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+ boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma);
}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index 8c7254c..db076da 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -150,6 +150,9 @@
if (intent != null) {
executeIntent(intent);
}
+
+ // This is asynchronous, but it gets processed by WebCore before it starts loading pages.
+ mWebView.useMockDeviceOrientation();
}
@Override
@@ -494,6 +497,12 @@
mGeolocationPermission = allow;
}
+ public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+ boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+ mWebView.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
+ canProvideGamma, gamma);
+ }
+
public void overridePreference(String key, boolean value) {
// TODO: We should look up the correct WebView for the frame which
// called the layoutTestController method. Currently, we just use the
diff --git a/tests/DumpRenderTree2/Android.mk b/tests/DumpRenderTree2/Android.mk
index 948ad72..eddbb4b 100644
--- a/tests/DumpRenderTree2/Android.mk
+++ b/tests/DumpRenderTree2/Android.mk
@@ -5,6 +5,8 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
LOCAL_STATIC_JAVA_LIBRARIES := diff_match_patch
LOCAL_PACKAGE_NAME := DumpRenderTree2
diff --git a/tests/DumpRenderTree2/AndroidManifest.xml b/tests/DumpRenderTree2/AndroidManifest.xml
index 9f6097a..dd0c4e9f 100644
--- a/tests/DumpRenderTree2/AndroidManifest.xml
+++ b/tests/DumpRenderTree2/AndroidManifest.xml
@@ -16,6 +16,8 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.dumprendertree2">
<application>
+ <uses-library android:name="android.test.runner" />
+
<activity android:name=".ui.DirListActivity"
android:label="Dump Render Tree 2"
android:configChanges="orientation">
@@ -25,8 +27,15 @@
</intent-filter>
</activity>
+ <!-- android:launchMode="singleTask" is there so we only have a one instance
+ of this activity. However, it doesn't seem to work exactly like described in the
+ documentation, because the behaviour of the application suggest
+ there is only a single task for all 3 activities. We don't understand
+ how exactly it all works, but at the moment it works just fine.
+ It can lead to some weird behaviour in the future. -->
<activity android:name=".TestsListActivity"
- android:label="Tests' list activity">
+ android:label="Tests' list activity"
+ android:launchMode="singleTask">
</activity>
<activity android:name=".LayoutTestsExecutor"
@@ -38,6 +47,10 @@
</service>
</application>
+ <instrumentation android:name="com.android.dumprendertree2.scriptsupport.ScriptTestRunner"
+ android:targetPackage="com.android.dumprendertree2"
+ android:label="Layout tests script runner" />
+
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_SDCARD" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
diff --git a/tests/DumpRenderTree2/assets/run_layout_tests.py b/tests/DumpRenderTree2/assets/run_layout_tests.py
new file mode 100644
index 0000000..b13d8c9
--- /dev/null
+++ b/tests/DumpRenderTree2/assets/run_layout_tests.py
@@ -0,0 +1,65 @@
+#!/usr/bin/python
+
+"""Run layout tests on the device.
+
+ It runs the specified tests on the device, downloads the summaries to the temporary directory
+ and opens html details in the default browser.
+
+ Usage:
+ run_layout_tests.py PATH
+"""
+
+import sys
+import os
+import subprocess
+import logging
+import webbrowser
+import tempfile
+
+#TODO: These should not be hardcoded
+RESULTS_ABSOLUTE_PATH = "/sdcard/android/LayoutTests-results/"
+DETAILS_HTML = "details.html"
+SUMMARY_TXT = "summary.txt"
+
+def main():
+ if len(sys.argv) > 1:
+ path = sys.argv[1]
+ else:
+ path = ""
+
+ logging.basicConfig(level=logging.INFO, format='%(message)s')
+
+ tmpdir = tempfile.gettempdir()
+
+ # Run the tests in path
+ cmd = "adb shell am instrument "
+ cmd += "-e class com.android.dumprendertree2.scriptsupport.Starter#startLayoutTests "
+ cmd += "-e path \"" + path + "\" "
+ cmd +="-w com.android.dumprendertree2/com.android.dumprendertree2.scriptsupport.ScriptTestRunner"
+
+ logging.info("Running the tests...")
+ subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
+
+ logging.info("Downloading the summaries...")
+
+ # Download the txt summary to tmp folder
+ summary_txt_tmp_path = os.path.join(tmpdir, SUMMARY_TXT)
+ cmd = "adb pull " + RESULTS_ABSOLUTE_PATH + SUMMARY_TXT + " " + summary_txt_tmp_path
+ subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
+
+ # Download the html summary to tmp folder
+ details_html_tmp_path = os.path.join(tmpdir, DETAILS_HTML)
+ cmd = "adb pull " + RESULTS_ABSOLUTE_PATH + DETAILS_HTML + " " + details_html_tmp_path
+ subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
+
+ # Print summary to console
+ logging.info("All done.\n")
+ cmd = "cat " + summary_txt_tmp_path
+ os.system(cmd)
+ logging.info("")
+
+ # Open the browser with summary
+ webbrowser.open(details_html_tmp_path)
+
+if __name__ == "__main__":
+ main();
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSender.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSender.java
new file mode 100644
index 0000000..5b7cbc4
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSender.java
@@ -0,0 +1,110 @@
+/*
+ * 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.dumprendertree2;
+
+import android.webkit.WebView;
+
+/**
+ * A class that acts as a JS interface for webview to mock various touch events,
+ * mouse actions and key presses.
+ *
+ * The methods here just call corresponding methods on EventSenderImpl
+ * that contains the logic of how to execute the methods.
+ */
+public class EventSender {
+ EventSenderImpl mEventSenderImpl = new EventSenderImpl();
+
+ public void reset(WebView webView) {
+ mEventSenderImpl.reset(webView);
+ }
+
+ public void enableDOMUIEventLogging(int domNode) {
+ mEventSenderImpl.enableDOMUIEventLogging(domNode);
+ }
+
+ public void fireKeyboardEventsToElement(int domNode) {
+ mEventSenderImpl.fireKeyboardEventsToElement(domNode);
+ }
+
+ public void keyDown(String character, String[] withModifiers) {
+ mEventSenderImpl.keyDown(character, withModifiers);
+ }
+
+ public void keyDown(String character) {
+ keyDown(character, null);
+ }
+
+ public void leapForward(int milliseconds) {
+ mEventSenderImpl.leapForward(milliseconds);
+ }
+
+ public void mouseClick() {
+ mEventSenderImpl.mouseClick();
+ }
+
+ public void mouseDown() {
+ mEventSenderImpl.mouseDown();
+ }
+
+ public void mouseMoveTo(int x, int y) {
+ mEventSenderImpl.mouseMoveTo(x, y);
+ }
+
+ public void mouseUp() {
+ mEventSenderImpl.mouseUp();
+ }
+
+ public void touchStart() {
+ mEventSenderImpl.touchStart();
+ }
+
+ public void addTouchPoint(int x, int y) {
+ mEventSenderImpl.addTouchPoint(x, y);
+ }
+
+ public void updateTouchPoint(int id, int x, int y) {
+ mEventSenderImpl.updateTouchPoint(id, x, y);
+ }
+
+ public void setTouchModifier(String modifier, boolean enabled) {
+ mEventSenderImpl.setTouchModifier(modifier, enabled);
+ }
+
+ public void touchMove() {
+ mEventSenderImpl.touchMove();
+ }
+
+ public void releaseTouchPoint(int id) {
+ mEventSenderImpl.releaseTouchPoint(id);
+ }
+
+ public void touchEnd() {
+ mEventSenderImpl.touchEnd();
+ }
+
+ public void touchCancel() {
+ mEventSenderImpl.touchCancel();
+ }
+
+ public void clearTouchPoints() {
+ mEventSenderImpl.clearTouchPoints();
+ }
+
+ public void cancelTouchPoint(int id) {
+ mEventSenderImpl.cancelTouchPoint(id);
+ }
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSenderImpl.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSenderImpl.java
new file mode 100644
index 0000000..93e6137
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSenderImpl.java
@@ -0,0 +1,563 @@
+/*
+ * 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.dumprendertree2;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.webkit.WebView;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * An implementation of EventSender
+ */
+public class EventSenderImpl {
+ private static final String LOG_TAG = "EventSenderImpl";
+
+ private static final int MSG_ENABLE_DOM_UI_EVENT_LOGGING = 0;
+ private static final int MSG_FIRE_KEYBOARD_EVENTS_TO_ELEMENT = 1;
+ private static final int MSG_LEAP_FORWARD = 2;
+
+ private static final int MSG_KEY_DOWN = 3;
+
+ private static final int MSG_MOUSE_DOWN = 4;
+ private static final int MSG_MOUSE_UP = 5;
+ private static final int MSG_MOUSE_CLICK = 6;
+ private static final int MSG_MOUSE_MOVE_TO = 7;
+
+ private static final int MSG_ADD_TOUCH_POINT = 8;
+ private static final int MSG_TOUCH_START = 9;
+ private static final int MSG_UPDATE_TOUCH_POINT = 10;
+ private static final int MSG_TOUCH_MOVE = 11;
+ private static final int MSG_CLEAR_TOUCH_POINTS = 12;
+ private static final int MSG_TOUCH_CANCEL = 13;
+ private static final int MSG_RELEASE_TOUCH_POINT = 14;
+ private static final int MSG_TOUCH_END = 15;
+ private static final int MSG_SET_TOUCH_MODIFIER = 16;
+ private static final int MSG_CANCEL_TOUCH_POINT = 17;
+
+ public static class TouchPoint {
+ WebView mWebView;
+ private int mX;
+ private int mY;
+ private long mDownTime;
+ private boolean mReleased = false;
+ private boolean mMoved = false;
+ private boolean mCancelled = false;
+
+ public TouchPoint(WebView webView, int x, int y) {
+ mWebView = webView;
+ mX = scaleX(x);
+ mY = scaleY(y);
+ }
+
+ public int getX() {
+ return mX;
+ }
+
+ public int getY() {
+ return mY;
+ }
+
+ public boolean hasMoved() {
+ return mMoved;
+ }
+
+ public void move(int newX, int newY) {
+ mX = scaleX(newX);
+ mY = scaleY(newY);
+ mMoved = true;
+ }
+
+ public void resetHasMoved() {
+ mMoved = false;
+ }
+
+ public long getDownTime() {
+ return mDownTime;
+ }
+
+ public void setDownTime(long downTime) {
+ mDownTime = downTime;
+ }
+
+ public boolean isReleased() {
+ return mReleased;
+ }
+
+ public void release() {
+ mReleased = true;
+ }
+
+ public boolean isCancelled() {
+ return mCancelled;
+ }
+
+ public void cancel() {
+ mCancelled = true;
+ }
+
+ private int scaleX(int x) {
+ return (int)(x * mWebView.getScale()) - mWebView.getScrollX();
+ }
+
+ private int scaleY(int y) {
+ return (int)(y * mWebView.getScale()) - mWebView.getScrollY();
+ }
+ }
+
+ private List<TouchPoint> mTouchPoints;
+ private int mTouchMetaState;
+ private int mMouseX;
+ private int mMouseY;
+
+ private WebView mWebView;
+
+ private Handler mEventSenderHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ TouchPoint touchPoint;
+ Bundle bundle;
+ KeyEvent event;
+
+ switch (msg.what) {
+ case MSG_ENABLE_DOM_UI_EVENT_LOGGING:
+ /** TODO: implement */
+ break;
+
+ case MSG_FIRE_KEYBOARD_EVENTS_TO_ELEMENT:
+ /** TODO: implement */
+ break;
+
+ case MSG_LEAP_FORWARD:
+ /** TODO: implement */
+ break;
+
+ case MSG_KEY_DOWN:
+ bundle = (Bundle)msg.obj;
+ String character = bundle.getString("character");
+ String[] withModifiers = bundle.getStringArray("withModifiers");
+
+ if (withModifiers != null && withModifiers.length > 0) {
+ for (int i = 0; i < withModifiers.length; i++) {
+ executeKeyEvent(KeyEvent.ACTION_DOWN,
+ modifierToKeyCode(withModifiers[i]));
+ }
+ }
+ executeKeyEvent(KeyEvent.ACTION_DOWN,
+ charToKeyCode(character.toLowerCase().toCharArray()[0]));
+ break;
+
+ /** MOUSE */
+
+ case MSG_MOUSE_DOWN:
+ /** TODO: Implement */
+ break;
+
+ case MSG_MOUSE_UP:
+ /** TODO: Implement */
+ break;
+
+ case MSG_MOUSE_CLICK:
+ /** TODO: Implement */
+ break;
+
+ case MSG_MOUSE_MOVE_TO:
+ int x = msg.arg1;
+ int y = msg.arg2;
+
+ event = null;
+ if (x > mMouseX) {
+ event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT);
+ } else if (x < mMouseX) {
+ event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT);
+ }
+ if (event != null) {
+ mWebView.onKeyDown(event.getKeyCode(), event);
+ mWebView.onKeyUp(event.getKeyCode(), event);
+ }
+
+ event = null;
+ if (y > mMouseY) {
+ event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN);
+ } else if (y < mMouseY) {
+ event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP);
+ }
+ if (event != null) {
+ mWebView.onKeyDown(event.getKeyCode(), event);
+ mWebView.onKeyUp(event.getKeyCode(), event);
+ }
+
+ mMouseX = x;
+ mMouseY = y;
+ break;
+
+ /** TOUCH */
+
+ case MSG_ADD_TOUCH_POINT:
+ getTouchPoints().add(new TouchPoint(mWebView,
+ msg.arg1, msg.arg2));
+ if (getTouchPoints().size() > 1) {
+ Log.w(LOG_TAG + "::MSG_ADD_TOUCH_POINT", "Added more than one touch point");
+ }
+ break;
+
+ case MSG_TOUCH_START:
+ /**
+ * FIXME: At the moment we don't support multi-touch. Hence, we only examine
+ * the first touch point. In future this method will need rewriting.
+ */
+ if (getTouchPoints().isEmpty()) {
+ return;
+ }
+ touchPoint = getTouchPoints().get(0);
+
+ touchPoint.setDownTime(SystemClock.uptimeMillis());
+ executeTouchEvent(touchPoint, MotionEvent.ACTION_DOWN);
+ break;
+
+ case MSG_UPDATE_TOUCH_POINT:
+ bundle = (Bundle)msg.obj;
+
+ int id = bundle.getInt("id");
+ if (id >= getTouchPoints().size()) {
+ Log.w(LOG_TAG + "::MSG_UPDATE_TOUCH_POINT", "TouchPoint out of bounds: "
+ + id);
+ break;
+ }
+
+ getTouchPoints().get(id).move(bundle.getInt("x"), bundle.getInt("y"));
+ break;
+
+ case MSG_TOUCH_MOVE:
+ /**
+ * FIXME: At the moment we don't support multi-touch. Hence, we only examine
+ * the first touch point. In future this method will need rewriting.
+ */
+ if (getTouchPoints().isEmpty()) {
+ return;
+ }
+ touchPoint = getTouchPoints().get(0);
+
+ if (!touchPoint.hasMoved()) {
+ return;
+ }
+ executeTouchEvent(touchPoint, MotionEvent.ACTION_MOVE);
+ touchPoint.resetHasMoved();
+ break;
+
+ case MSG_CANCEL_TOUCH_POINT:
+ if (msg.arg1 >= getTouchPoints().size()) {
+ Log.w(LOG_TAG + "::MSG_RELEASE_TOUCH_POINT", "TouchPoint out of bounds: "
+ + msg.arg1);
+ break;
+ }
+
+ getTouchPoints().get(msg.arg1).cancel();
+ break;
+
+ case MSG_TOUCH_CANCEL:
+ /**
+ * FIXME: At the moment we don't support multi-touch. Hence, we only examine
+ * the first touch point. In future this method will need rewriting.
+ */
+ if (getTouchPoints().isEmpty()) {
+ return;
+ }
+ touchPoint = getTouchPoints().get(0);
+
+ if (touchPoint.isCancelled()) {
+ executeTouchEvent(touchPoint, MotionEvent.ACTION_CANCEL);
+ }
+ break;
+
+ case MSG_RELEASE_TOUCH_POINT:
+ if (msg.arg1 >= getTouchPoints().size()) {
+ Log.w(LOG_TAG + "::MSG_RELEASE_TOUCH_POINT", "TouchPoint out of bounds: "
+ + msg.arg1);
+ break;
+ }
+
+ getTouchPoints().get(msg.arg1).release();
+ break;
+
+ case MSG_TOUCH_END:
+ /**
+ * FIXME: At the moment we don't support multi-touch. Hence, we only examine
+ * the first touch point. In future this method will need rewriting.
+ */
+ if (getTouchPoints().isEmpty()) {
+ return;
+ }
+ touchPoint = getTouchPoints().get(0);
+
+ executeTouchEvent(touchPoint, MotionEvent.ACTION_UP);
+ if (touchPoint.isReleased()) {
+ getTouchPoints().remove(0);
+ touchPoint = null;
+ }
+ break;
+
+ case MSG_SET_TOUCH_MODIFIER:
+ bundle = (Bundle)msg.obj;
+ String modifier = bundle.getString("modifier");
+ boolean enabled = bundle.getBoolean("enabled");
+
+ int mask = 0;
+ if ("alt".equals(modifier.toLowerCase())) {
+ mask = KeyEvent.META_ALT_ON;
+ } else if ("shift".equals(modifier.toLowerCase())) {
+ mask = KeyEvent.META_SHIFT_ON;
+ } else if ("ctrl".equals(modifier.toLowerCase())) {
+ mask = KeyEvent.META_SYM_ON;
+ }
+
+ if (enabled) {
+ mTouchMetaState |= mask;
+ } else {
+ mTouchMetaState &= ~mask;
+ }
+
+ break;
+
+ case MSG_CLEAR_TOUCH_POINTS:
+ getTouchPoints().clear();
+ break;
+
+ default:
+ break;
+ }
+ }
+ };
+
+ public void reset(WebView webView) {
+ mWebView = webView;
+ mTouchPoints = null;
+ mTouchMetaState = 0;
+ mMouseX = 0;
+ mMouseY = 0;
+ }
+
+ public void enableDOMUIEventLogging(int domNode) {
+ Message msg = mEventSenderHandler.obtainMessage(MSG_ENABLE_DOM_UI_EVENT_LOGGING);
+ msg.arg1 = domNode;
+ msg.sendToTarget();
+ }
+
+ public void fireKeyboardEventsToElement(int domNode) {
+ Message msg = mEventSenderHandler.obtainMessage(MSG_FIRE_KEYBOARD_EVENTS_TO_ELEMENT);
+ msg.arg1 = domNode;
+ msg.sendToTarget();
+ }
+
+ public void leapForward(int milliseconds) {
+ Message msg = mEventSenderHandler.obtainMessage(MSG_LEAP_FORWARD);
+ msg.arg1 = milliseconds;
+ msg.sendToTarget();
+ }
+
+ public void keyDown(String character, String[] withModifiers) {
+ Bundle bundle = new Bundle();
+ bundle.putString("character", character);
+ bundle.putStringArray("withModifiers", withModifiers);
+ mEventSenderHandler.obtainMessage(MSG_KEY_DOWN, bundle).sendToTarget();
+ }
+
+ /** MOUSE */
+
+ public void mouseDown() {
+ mEventSenderHandler.sendEmptyMessage(MSG_MOUSE_DOWN);
+ }
+
+ public void mouseUp() {
+ mEventSenderHandler.sendEmptyMessage(MSG_MOUSE_UP);
+ }
+
+ public void mouseClick() {
+ mEventSenderHandler.sendEmptyMessage(MSG_MOUSE_CLICK);
+ }
+
+ public void mouseMoveTo(int x, int y) {
+ mEventSenderHandler.obtainMessage(MSG_MOUSE_MOVE_TO, x, y).sendToTarget();
+ }
+
+ /** TOUCH */
+
+ public void addTouchPoint(int x, int y) {
+ mEventSenderHandler.obtainMessage(MSG_ADD_TOUCH_POINT, x, y).sendToTarget();
+ }
+
+ public void touchStart() {
+ mEventSenderHandler.sendEmptyMessage(MSG_TOUCH_START);
+ }
+
+ public void updateTouchPoint(int id, int x, int y) {
+ Bundle bundle = new Bundle();
+ bundle.putInt("id", id);
+ bundle.putInt("x", x);
+ bundle.putInt("y", y);
+ mEventSenderHandler.obtainMessage(MSG_UPDATE_TOUCH_POINT, bundle).sendToTarget();
+ }
+
+ public void touchMove() {
+ mEventSenderHandler.sendEmptyMessage(MSG_TOUCH_MOVE);
+ }
+
+ public void cancelTouchPoint(int id) {
+ Message msg = mEventSenderHandler.obtainMessage(MSG_CANCEL_TOUCH_POINT);
+ msg.arg1 = id;
+ msg.sendToTarget();
+ }
+
+ public void touchCancel() {
+ mEventSenderHandler.sendEmptyMessage(MSG_TOUCH_CANCEL);
+ }
+
+ public void releaseTouchPoint(int id) {
+ Message msg = mEventSenderHandler.obtainMessage(MSG_RELEASE_TOUCH_POINT);
+ msg.arg1 = id;
+ msg.sendToTarget();
+ }
+
+ public void touchEnd() {
+ mEventSenderHandler.sendEmptyMessage(MSG_TOUCH_END);
+ }
+
+ public void setTouchModifier(String modifier, boolean enabled) {
+ Bundle bundle = new Bundle();
+ bundle.putString("modifier", modifier);
+ bundle.putBoolean("enabled", enabled);
+ mEventSenderHandler.obtainMessage(MSG_SET_TOUCH_MODIFIER, bundle).sendToTarget();
+ }
+
+ public void clearTouchPoints() {
+ mEventSenderHandler.sendEmptyMessage(MSG_CLEAR_TOUCH_POINTS);
+ }
+
+ private List<TouchPoint> getTouchPoints() {
+ if (mTouchPoints == null) {
+ mTouchPoints = new LinkedList<TouchPoint>();
+ }
+
+ return mTouchPoints;
+ }
+
+ private void executeTouchEvent(TouchPoint touchPoint, int action) {
+ MotionEvent event =
+ MotionEvent.obtain(touchPoint.getDownTime(), SystemClock.uptimeMillis(),
+ action, touchPoint.getX(), touchPoint.getY(), mTouchMetaState);
+ mWebView.onTouchEvent(event);
+ }
+
+ private void executeKeyEvent(int action, int keyCode) {
+ KeyEvent event = new KeyEvent(action, keyCode);
+ mWebView.onKeyDown(event.getKeyCode(), event);
+ }
+
+ /**
+ * Assumes lowercase chars, case needs to be handled by calling function.
+ */
+ private static int charToKeyCode(char c) {
+ // handle numbers
+ if (c >= '0' && c <= '9') {
+ int offset = c - '0';
+ return KeyEvent.KEYCODE_0 + offset;
+ }
+
+ // handle characters
+ if (c >= 'a' && c <= 'z') {
+ int offset = c - 'a';
+ return KeyEvent.KEYCODE_A + offset;
+ }
+
+ // handle all others
+ switch (c) {
+ case '*':
+ return KeyEvent.KEYCODE_STAR;
+
+ case '#':
+ return KeyEvent.KEYCODE_POUND;
+
+ case ',':
+ return KeyEvent.KEYCODE_COMMA;
+
+ case '.':
+ return KeyEvent.KEYCODE_PERIOD;
+
+ case '\t':
+ return KeyEvent.KEYCODE_TAB;
+
+ case ' ':
+ return KeyEvent.KEYCODE_SPACE;
+
+ case '\n':
+ return KeyEvent.KEYCODE_ENTER;
+
+ case '\b':
+ case 0x7F:
+ return KeyEvent.KEYCODE_DEL;
+
+ case '~':
+ return KeyEvent.KEYCODE_GRAVE;
+
+ case '-':
+ return KeyEvent.KEYCODE_MINUS;
+
+ case '=':
+ return KeyEvent.KEYCODE_EQUALS;
+
+ case '(':
+ return KeyEvent.KEYCODE_LEFT_BRACKET;
+
+ case ')':
+ return KeyEvent.KEYCODE_RIGHT_BRACKET;
+
+ case '\\':
+ return KeyEvent.KEYCODE_BACKSLASH;
+
+ case ';':
+ return KeyEvent.KEYCODE_SEMICOLON;
+
+ case '\'':
+ return KeyEvent.KEYCODE_APOSTROPHE;
+
+ case '/':
+ return KeyEvent.KEYCODE_SLASH;
+
+ default:
+ return c;
+ }
+ }
+
+ private static int modifierToKeyCode(String modifier) {
+ if (modifier.equals("ctrlKey")) {
+ return KeyEvent.KEYCODE_ALT_LEFT;
+ } else if (modifier.equals("shiftKey")) {
+ return KeyEvent.KEYCODE_SHIFT_LEFT;
+ } else if (modifier.equals("altKey")) {
+ return KeyEvent.KEYCODE_SYM;
+ }
+
+ return KeyEvent.KEYCODE_UNKNOWN;
+ }
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
index 8ff5e63..6db9571 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
@@ -89,4 +89,12 @@
Log.w(LOG_TAG + "::setMockGeolocationError", "code: " + code + " message: " + message);
MockGeolocation.getInstance().setError(code, message);
}
-}
\ No newline at end of file
+
+ public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+ boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+ // Configuration is in WebKit, so stay on WebCore thread, but go via LayoutTestsExecutor
+ // as we need access to the Webview.
+ mLayoutTestsExecutor.setMockDeviceOrientation(
+ canProvideAlpha, alpha, canProvideBeta, beta, canProvideGamma, gamma);
+ }
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
index dc82c34..8cc4921 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
@@ -110,6 +110,8 @@
private boolean mSetGeolocationPermissionCalled;
private boolean mGeolocationPermission;
+ private EventSender mEventSender = new EventSender();
+
private WakeLock mScreenDimLock;
/** COMMUNICATION WITH ManagerService */
@@ -248,7 +250,7 @@
mCurrentTestIndex = intent.getIntExtra(EXTRA_TEST_INDEX, -1);
mTotalTestCount = mCurrentTestIndex + mTestsList.size();
- PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+ PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
mScreenDimLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK
| PowerManager.ON_AFTER_RELEASE, "WakeLock in LayoutTester");
mScreenDimLock.acquire();
@@ -269,6 +271,8 @@
mCurrentWebView = new WebView(this);
setupWebView(mCurrentWebView);
+ mEventSender.reset(mCurrentWebView);
+
setContentView(mCurrentWebView);
if (previousWebView != null) {
Log.d(LOG_TAG + "::reset", "previousWebView != null");
@@ -280,6 +284,7 @@
webView.setWebViewClient(mWebViewClient);
webView.setWebChromeClient(mWebChromeClient);
webView.addJavascriptInterface(mLayoutTestController, "layoutTestController");
+ webView.addJavascriptInterface(mEventSender, "eventSender");
/**
* Setting a touch interval of -1 effectively disables the optimisation in WebView
@@ -303,6 +308,9 @@
webViewSettings.setDomStorageEnabled(true);
webViewSettings.setWorkersEnabled(false);
webViewSettings.setXSSAuditorEnabled(false);
+
+ // This is asynchronous, but it gets processed by WebCore before it starts loading pages.
+ mCurrentWebView.useMockDeviceOrientation();
}
private void startTests() {
@@ -462,8 +470,7 @@
Handler mLayoutTestControllerHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
- assert mCurrentState.isRunningState()
- : "mCurrentState = " + mCurrentState.name();
+ assert mCurrentState.isRunningState() : "mCurrentState = " + mCurrentState.name();
switch (msg.what) {
case MSG_WAIT_UNTIL_DONE:
@@ -565,4 +572,10 @@
msg.arg1 = allow ? 1 : 0;
msg.sendToTarget();
}
-}
\ No newline at end of file
+
+ public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+ boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+ mCurrentWebView.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
+ canProvideGamma, gamma);
+ }
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
index 3bbfb89..7ec3dd7c 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
@@ -82,6 +82,7 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_FIRST_TEST:
+ mSummarizer.reset();
Bundle bundle = msg.getData();
ensureNextTestSetup(bundle.getString("firstTest"), bundle.getInt("index"));
break;
@@ -93,6 +94,11 @@
case MSG_ALL_TESTS_FINISHED:
mSummarizer.summarize();
+ Intent intent = new Intent(ManagerService.this, TestsListActivity.class);
+ intent.setAction(Intent.ACTION_SHUTDOWN);
+ /** This flag is needed because we send the intent from the service */
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
break;
}
}
@@ -166,7 +172,8 @@
Intent intent = new Intent(this, TestsListActivity.class);
intent.setAction(Intent.ACTION_REBOOT);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ /** This flag is needed because we send the intent from the service */
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("crashedTestIndex", mCurrentlyRunningTestIndex);
startActivity(intent);
}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
index 1b73f97..e27ecc9 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
@@ -166,7 +166,8 @@
"</script>";
/** TODO: Make it a setting */
- private static final String HTML_SUMMARY_RELATIVE_PATH = "summary.html";
+ private static final String HTML_DETAILS_RELATIVE_PATH = "details.html";
+ private static final String TXT_SUMMARY_RELATIVE_PATH = "summary.txt";
private int mCrashedTestsCount = 0;
private List<AbstractResult> mFailedNotIgnoredTests = new ArrayList<AbstractResult>();
@@ -176,6 +177,8 @@
private FileFilter mFileFilter;
private String mResultsRootDirPath;
+ private String mTitleString;
+
public Summarizer(FileFilter fileFilter, String resultsRootDirPath) {
mFileFilter = fileFilter;
mResultsRootDirPath = resultsRootDirPath;
@@ -198,6 +201,35 @@
}
public void summarize() {
+ createHtmlDetails();
+ createTxtSummary();
+ }
+
+ public void reset() {
+ mCrashedTestsCount = 0;
+ mFailedNotIgnoredTests.clear();
+ mIgnoredTests.clear();
+ mPassedNotIgnoredTests.clear();
+ mTitleString = null;
+ }
+
+ private void createTxtSummary() {
+ StringBuilder txt = new StringBuilder();
+
+ txt.append(getTitleString() + "\n");
+ if (mCrashedTestsCount > 0) {
+ txt.append("CRASHED (total among all tests): " + mCrashedTestsCount + "\n");
+ txt.append("-------------");
+ }
+ txt.append("FAILED: " + mFailedNotIgnoredTests.size() + "\n");
+ txt.append("IGNORED: " + mIgnoredTests.size() + "\n");
+ txt.append("PASSED: " + mPassedNotIgnoredTests.size() + "\n");
+
+ FsUtils.writeDataToStorage(new File(mResultsRootDirPath, TXT_SUMMARY_RELATIVE_PATH),
+ txt.toString().getBytes(), false);
+ }
+
+ private void createHtmlDetails() {
StringBuilder html = new StringBuilder();
html.append("<html><head>");
@@ -215,17 +247,24 @@
html.append("</body></html>");
- FsUtils.writeDataToStorage(new File(mResultsRootDirPath, HTML_SUMMARY_RELATIVE_PATH),
+ FsUtils.writeDataToStorage(new File(mResultsRootDirPath, HTML_DETAILS_RELATIVE_PATH),
html.toString().getBytes(), false);
}
+ private String getTitleString() {
+ if (mTitleString == null) {
+ int total = mFailedNotIgnoredTests.size() +
+ mPassedNotIgnoredTests.size() +
+ mIgnoredTests.size();
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
+ mTitleString = " - total of " + total + " tests - " + dateFormat.format(new Date());
+ }
+
+ return mTitleString;
+ }
+
private void createTopSummaryTable(StringBuilder html) {
- int total = mFailedNotIgnoredTests.size() +
- mPassedNotIgnoredTests.size() +
- mIgnoredTests.size();
- SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
- html.append("<h1> - total of " + total + " tests - ");
- html.append(dateFormat.format(new Date()) + "</h1>");
+ html.append("<h1>" + getTitleString() + "</h1>");
html.append("<table class=\"summary\">");
createSummaryTableRow(html, "CRASHED", mCrashedTestsCount);
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java
index c21463b..c95199f 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java
@@ -24,6 +24,8 @@
import android.os.Message;
import android.view.Window;
+import com.android.dumprendertree2.scriptsupport.OnEverythingFinishedCallback;
+
import java.util.ArrayList;
/**
@@ -57,6 +59,9 @@
private ArrayList<String> mTestsList;
private int mTotalTestCount;
+ private OnEverythingFinishedCallback mOnEverythingFinishedCallback;
+ private boolean mEverythingFinished;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -85,6 +90,15 @@
new TestsListPreloaderThread(path, doneMsg).start();
}
+ @Override
+ protected void onNewIntent(Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_REBOOT)) {
+ onCrashIntent(intent);
+ } else if (intent.getAction().equals(Intent.ACTION_SHUTDOWN)) {
+ onEverythingFinishedIntent(intent);
+ }
+ }
+
/**
* This method handles an intent that comes from ManageService when crash is detected.
* The intent contains an index in mTestsList of the test that crashed. TestsListActivity
@@ -94,18 +108,28 @@
* LayoutTestExecutor runs then as usual, sending reports to ManagerService. If it
* detects the crash it sends a new intent and the flow repeats.
*/
- @Override
- protected void onNewIntent(Intent intent) {
- if (!intent.getAction().equals(Intent.ACTION_REBOOT)) {
- return;
- }
-
+ private void onCrashIntent(Intent intent) {
int nextTestToRun = intent.getIntExtra("crashedTestIndex", -1) + 1;
if (nextTestToRun > 0 && nextTestToRun <= mTotalTestCount) {
restartExecutor(nextTestToRun);
}
}
+ public void registerOnEverythingFinishedCallback(OnEverythingFinishedCallback callback) {
+ mOnEverythingFinishedCallback = callback;
+ if (mEverythingFinished) {
+ mOnEverythingFinishedCallback.onFinished();
+ }
+ }
+
+ private void onEverythingFinishedIntent(Intent intent) {
+ /** TODO: Show some kind of summary to the user */
+ mEverythingFinished = true;
+ if (mOnEverythingFinishedCallback != null) {
+ mOnEverythingFinishedCallback.onFinished();
+ }
+ }
+
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putStringArrayList("testsList", mTestsList);
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java
index f76105d..2145af7 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java
@@ -68,15 +68,15 @@
File file = new File(TESTS_ROOT_DIR_PATH, mRelativePath);
if (!file.exists()) {
Log.e(LOG_TAG + "::run", "Path does not exist: " + mRelativePath);
- return;
+ } else {
+ /** Populate the tests' list accordingly */
+ if (file.isDirectory()) {
+ preloadTests(mRelativePath);
+ } else {
+ mTestsList.add(mRelativePath);
+ }
}
- /** Populate the tests' list accordingly */
- if (file.isDirectory()) {
- preloadTests(mRelativePath);
- } else {
- mTestsList.add(mRelativePath);
- }
mDoneMsg.obj = mTestsList;
mDoneMsg.sendToTarget();
}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/OnEverythingFinishedCallback.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/OnEverythingFinishedCallback.java
new file mode 100644
index 0000000..e1d4364
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/OnEverythingFinishedCallback.java
@@ -0,0 +1,25 @@
+/*
+ * 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.dumprendertree2.scriptsupport;
+
+/**
+ * Callback used to inform scriptsupport.Starter that everything is finished and
+ * we can exit
+ */
+public interface OnEverythingFinishedCallback {
+ public void onFinished();
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/ScriptTestRunner.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/ScriptTestRunner.java
new file mode 100644
index 0000000..78f58d5
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/ScriptTestRunner.java
@@ -0,0 +1,37 @@
+/*
+ * 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.dumprendertree2.scriptsupport;
+
+import android.os.Bundle;
+import android.test.InstrumentationTestRunner;
+
+/**
+ * Extends InstrumentationTestRunner to allow the script to pass arguments to the application
+ */
+public class ScriptTestRunner extends InstrumentationTestRunner {
+ String mTestsRelativePath;
+
+ @Override
+ public void onCreate(Bundle arguments) {
+ mTestsRelativePath = arguments.getString("path");
+ super.onCreate(arguments);
+ }
+
+ public String getTestsRelativePath() {
+ return mTestsRelativePath;
+ }
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java
new file mode 100644
index 0000000..ddfae69
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java
@@ -0,0 +1,74 @@
+/*
+ * 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.dumprendertree2.scriptsupport;
+
+import android.content.Intent;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import com.android.dumprendertree2.TestsListActivity;
+
+/**
+ * A class which provides methods that can be invoked by a script running on the host machine to
+ * run the tests.
+ *
+ * It starts a TestsListActivity and does not return until all the tests finish executing.
+ */
+public class Starter extends ActivityInstrumentationTestCase2<TestsListActivity> {
+ private static final String LOG_TAG = "Starter";
+ private boolean mEverythingFinished;
+
+ public Starter() {
+ super(TestsListActivity.class);
+ }
+
+ /**
+ * This method is called from adb to start executing the tests. It doesn't return
+ * until everything is finished so that the script can wait for the end if it needs
+ * to.
+ */
+ public void startLayoutTests() {
+ ScriptTestRunner runner = (ScriptTestRunner)getInstrumentation();
+ String relativePath = runner.getTestsRelativePath();
+
+ Intent intent = new Intent();
+ intent.setClassName("com.android.dumprendertree2", "TestsListActivity");
+ intent.setAction(Intent.ACTION_RUN);
+ intent.putExtra(TestsListActivity.EXTRA_TEST_PATH, relativePath);
+ setActivityIntent(intent);
+ getActivity().registerOnEverythingFinishedCallback(new OnEverythingFinishedCallback() {
+ /** This method is safe to call on any thread */
+ @Override
+ public void onFinished() {
+ synchronized (Starter.this) {
+ mEverythingFinished = true;
+ Starter.this.notifyAll();
+ }
+ }
+ });
+
+ synchronized (this) {
+ while (!mEverythingFinished) {
+ try {
+ this.wait();
+ } catch (InterruptedException e) {
+ Log.e(LOG_TAG + "::startLayoutTests", e.getMessage());
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java
index 661a8ec..af0d7d1 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java
@@ -378,6 +378,10 @@
private ListItem[] getDirList(String dirPath) {
File dir = new File(mRootDirPath, dirPath);
+ if (!dir.exists()) {
+ return new ListItem[0];
+ }
+
List<ListItem> subDirs = new ArrayList<ListItem>();
List<ListItem> subFiles = new ArrayList<ListItem>();
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 73994f7..181b4c8 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -171,5 +171,23 @@
</intent-filter>
</activity>
+ <activity
+ android:name="SimplePathsActivity"
+ android:label="_SimplePaths">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="AdvancedBlendActivity"
+ android:label="_AdvancedBlend">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
</application>
</manifest>
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/AdvancedBlendActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/AdvancedBlendActivity.java
new file mode 100644
index 0000000..6c80a6d
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/AdvancedBlendActivity.java
@@ -0,0 +1,138 @@
+/*
+ * 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.google.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ComposeShader;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class AdvancedBlendActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(new ShadersView(this));
+ }
+
+ static class ShadersView extends View {
+ private BitmapShader mScaledShader;
+ private int mTexWidth;
+ private int mTexHeight;
+ private Paint mPaint;
+ private float mDrawWidth;
+ private float mDrawHeight;
+ private LinearGradient mHorGradient;
+ private ComposeShader mComposeShader;
+ private ComposeShader mCompose2Shader;
+ private ComposeShader mCompose3Shader;
+ private ComposeShader mCompose4Shader;
+ private ComposeShader mCompose5Shader;
+ private ComposeShader mCompose6Shader;
+ private BitmapShader mScaled2Shader;
+
+ ShadersView(Context c) {
+ super(c);
+
+ Bitmap texture = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+ mTexWidth = texture.getWidth();
+ mTexHeight = texture.getHeight();
+ mDrawWidth = mTexWidth * 2.2f;
+ mDrawHeight = mTexHeight * 1.2f;
+
+ mScaledShader = new BitmapShader(texture, Shader.TileMode.MIRROR,
+ Shader.TileMode.MIRROR);
+ Matrix m2 = new Matrix();
+ m2.setScale(0.5f, 0.5f);
+ mScaledShader.setLocalMatrix(m2);
+
+ mScaled2Shader = new BitmapShader(texture, Shader.TileMode.MIRROR,
+ Shader.TileMode.MIRROR);
+ Matrix m3 = new Matrix();
+ m3.setScale(0.1f, 0.1f);
+ mScaled2Shader.setLocalMatrix(m3);
+
+ mHorGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth, 0.0f,
+ Color.BLACK, Color.WHITE, Shader.TileMode.CLAMP);
+
+ mComposeShader = new ComposeShader(mScaledShader, mHorGradient,
+ PorterDuff.Mode.DARKEN);
+ mCompose2Shader = new ComposeShader(mScaledShader, mHorGradient,
+ PorterDuff.Mode.LIGHTEN);
+ mCompose3Shader = new ComposeShader(mScaledShader, mHorGradient,
+ PorterDuff.Mode.MULTIPLY);
+ mCompose4Shader = new ComposeShader(mScaledShader, mHorGradient,
+ PorterDuff.Mode.SCREEN);
+ mCompose5Shader = new ComposeShader(mScaledShader, mHorGradient,
+ PorterDuff.Mode.ADD);
+ mCompose6Shader = new ComposeShader(mHorGradient, mScaledShader,
+ PorterDuff.Mode.OVERLAY);
+
+ mPaint = new Paint();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ canvas.drawRGB(255, 255, 255);
+
+ canvas.save();
+ canvas.translate(40.0f, 40.0f);
+
+ mPaint.setShader(mComposeShader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.translate(0.0f, 40.0f + mDrawHeight);
+ mPaint.setShader(mCompose2Shader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.translate(0.0f, 40.0f + mDrawHeight);
+ mPaint.setShader(mCompose3Shader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.restore();
+
+ canvas.save();
+ canvas.translate(40.0f + mDrawWidth + 40.0f, 40.0f);
+
+ mPaint.setShader(mCompose4Shader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.translate(0.0f, 40.0f + mDrawHeight);
+ mPaint.setShader(mCompose5Shader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.translate(0.0f, 40.0f + mDrawHeight);
+ mPaint.setShader(mCompose6Shader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.restore();
+ }
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/SimplePathsActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/SimplePathsActivity.java
new file mode 100644
index 0000000..071a118
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/SimplePathsActivity.java
@@ -0,0 +1,40 @@
+/*
+ * 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.google.android.test.hwui;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class SimplePathsActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ FrameLayout layout = new FrameLayout(this);
+ EditText text = new EditText(this);
+ layout.addView(text, new FrameLayout.LayoutParams(600, 350, Gravity.CENTER));
+ text.setText("This is an example of an EditText widget \n" +
+ "using simple paths to create the selection.");
+ //text.setSelection(0, text.getText().length());
+
+ setContentView(layout);
+ }
+}
\ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java
index 3b5cf43..59f665c 100644
--- a/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java
@@ -35,6 +35,7 @@
static class CustomTextView extends View {
private final Paint mMediumPaint;
private final Paint mLargePaint;
+ private final Paint mStrikePaint;
CustomTextView(Context c) {
super(c);
@@ -45,6 +46,11 @@
mLargePaint = new Paint();
mLargePaint.setAntiAlias(true);
mLargePaint.setTextSize(36.0f);
+ mStrikePaint = new Paint();
+ mStrikePaint.setAntiAlias(true);
+ mStrikePaint.setTextSize(16.0f);
+ mStrikePaint.setUnderlineText(true);
+
}
@Override
@@ -61,6 +67,15 @@
canvas.drawText("Hello OpenGL renderer!", 100, 100, mMediumPaint);
canvas.drawText("Hello OpenGL renderer!", 100, 200, mLargePaint);
+
+ canvas.drawText("Hello OpenGL renderer!", 500, 40, mStrikePaint);
+ mStrikePaint.setStrikeThruText(true);
+ canvas.drawText("Hello OpenGL renderer!", 500, 70, mStrikePaint);
+ mStrikePaint.setUnderlineText(false);
+ canvas.drawText("Hello OpenGL renderer!", 500, 100, mStrikePaint);
+ mStrikePaint.setStrikeThruText(false);
+ mStrikePaint.setUnderlineText(true);
+
canvas.save();
canvas.clipRect(150.0f, 220.0f, 450.0f, 320.0f);
canvas.drawText("Hello OpenGL renderer!", 100, 300, mLargePaint);
diff --git a/voip/java/android/net/sip/BinderHelper.java b/voip/java/android/net/sip/BinderHelper.java
deleted file mode 100644
index bd3da32..0000000
--- a/voip/java/android/net/sip/BinderHelper.java
+++ /dev/null
@@ -1,79 +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.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.ConditionVariable;
-import android.os.IBinder;
-import android.os.IInterface;
-import android.os.Looper;
-import android.util.Log;
-
-// TODO: throw away this class after moving SIP classes to framework
-// This class helps to get IBinder instance of a service in a blocking call.
-// The method cannot be called in app's main thread as the ServiceConnection
-// callback will.
-class BinderHelper<T extends IInterface> {
- private Context mContext;
- private IBinder mBinder;
- private Class<T> mClass;
-
- BinderHelper(Context context, Class<T> klass) {
- mContext = context;
- mClass = klass;
- }
-
- void startService() {
- mContext.startService(new Intent(mClass.getName()));
- }
-
- void stopService() {
- mContext.stopService(new Intent(mClass.getName()));
- }
-
- IBinder getBinder() {
- // cannot call this method in app's main thread
- if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
- throw new RuntimeException(
- "This method cannot be called in app's main thread");
- }
-
- final ConditionVariable cv = new ConditionVariable();
- cv.close();
- ServiceConnection c = new ServiceConnection() {
- public synchronized void onServiceConnected(
- ComponentName className, IBinder binder) {
- Log.v("BinderHelper", "service connected!");
- mBinder = binder;
- cv.open();
- mContext.unbindService(this);
- }
-
- public void onServiceDisconnected(ComponentName className) {
- cv.open();
- mContext.unbindService(this);
- }
- };
- if (mContext.bindService(new Intent(mClass.getName()), c, 0)) {
- cv.block(4500);
- }
- return mBinder;
- }
-}
diff --git a/voip/java/android/net/sip/SipAudioCallImpl.java b/voip/java/android/net/sip/SipAudioCallImpl.java
index 57e0bd2..474bc4b 100644
--- a/voip/java/android/net/sip/SipAudioCallImpl.java
+++ b/voip/java/android/net/sip/SipAudioCallImpl.java
@@ -343,8 +343,11 @@
public synchronized void endCall() throws SipException {
try {
stopRinging();
- if (mSipSession != null) mSipSession.endCall();
stopCall(true);
+ mInCall = false;
+
+ // perform the above local ops first and then network op
+ if (mSipSession != null) mSipSession.endCall();
} catch (Throwable e) {
throwSipException(e);
}
diff --git a/voip/java/android/net/sip/SipManager.java b/voip/java/android/net/sip/SipManager.java
index f28b41c..287a13a 100644
--- a/voip/java/android/net/sip/SipManager.java
+++ b/voip/java/android/net/sip/SipManager.java
@@ -68,9 +68,6 @@
private ISipService mSipService;
- // Will be removed once the SIP service is integrated into framework
- private BinderHelper<ISipService> mBinderHelper;
-
/**
* Creates a manager instance and initializes the background SIP service.
* Will be removed once the SIP service is integrated into framework.
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 6e0bc9d..3426af7 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -85,5 +85,13 @@
WifiConfiguration getWifiApConfiguration();
void setWifiApConfiguration(in WifiConfiguration wifiConfig);
+
+ void startWifi();
+
+ void stopWifi();
+
+ void addToBlacklist(String bssid);
+
+ void clearBlacklist();
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 6fac902..339763a 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -293,6 +293,21 @@
public static final String EXTRA_NEW_RSSI = "newRssi";
/**
+ * Broadcast intent action indicating that the IP configuration
+ * changed on wifi.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String CONFIG_CHANGED_ACTION = "android.net.wifi.CONFIG_CHANGED";
+ /**
+ * The lookup key for a {@link android.net.NetworkProperties} object associated with the
+ * Wi-Fi network. Retrieve with
+ * {@link android.content.Intent#getParcelableExtra(String)}.
+ * @hide
+ */
+ public static final String EXTRA_NETWORK_PROPERTIES = "networkProperties";
+
+ /**
* The network IDs of the configured networks could have changed.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@@ -838,6 +853,82 @@
}
}
+ /**
+ * Start the driver and connect to network.
+ *
+ * This function will over-ride WifiLock and device idle status. For example,
+ * even if the device is idle or there is only a scan-only lock held,
+ * a start wifi would mean that wifi connection is kept active until
+ * a stopWifi() is sent.
+ *
+ * This API is used by WifiStateTracker
+ *
+ * @return {@code true} if the operation succeeds else {@code false}
+ * @hide
+ */
+ public boolean startWifi() {
+ try {
+ mService.startWifi();
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Disconnect from a network (if any) and stop the driver.
+ *
+ * This function will over-ride WifiLock and device idle status. Wi-Fi
+ * stays inactive until a startWifi() is issued.
+ *
+ * This API is used by WifiStateTracker
+ *
+ * @return {@code true} if the operation succeeds else {@code false}
+ * @hide
+ */
+ public boolean stopWifi() {
+ try {
+ mService.stopWifi();
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Add a bssid to the supplicant blacklist
+ *
+ * This API is used by WifiWatchdogService
+ *
+ * @return {@code true} if the operation succeeds else {@code false}
+ * @hide
+ */
+ public boolean addToBlacklist(String bssid) {
+ try {
+ mService.addToBlacklist(bssid);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Clear the supplicant blacklist
+ *
+ * This API is used by WifiWatchdogService
+ *
+ * @return {@code true} if the operation succeeds else {@code false}
+ * @hide
+ */
+ public boolean clearBlacklist() {
+ try {
+ mService.clearBlacklist();
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
/**
* Allows an application to keep the Wi-Fi radio awake.
* Normally the Wi-Fi radio may turn off when the user has not used the device in a while.
diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java
index f2f8343..af3132f 100644
--- a/wifi/java/android/net/wifi/WifiMonitor.java
+++ b/wifi/java/android/net/wifi/WifiMonitor.java
@@ -19,14 +19,13 @@
import android.util.Log;
import android.util.Config;
import android.net.NetworkInfo;
-import android.net.NetworkStateTracker;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
/**
* Listens for events from the wpa_supplicant server, and passes them on
- * to the {@link WifiStateTracker} for handling. Runs in its own thread.
+ * to the {@link WifiStateMachine} for handling. Runs in its own thread.
*
* @hide
*/
@@ -117,7 +116,7 @@
private static Pattern mConnectedEventPattern =
Pattern.compile("((?:[0-9a-f]{2}:){5}[0-9a-f]{2}) .* \\[id=([0-9]+) ");
- private final WifiStateTracker mWifiStateTracker;
+ private final WifiStateMachine mWifiStateMachine;
/**
* This indicates the supplicant connection for the monitor is closed
@@ -139,18 +138,14 @@
*/
private static final int MAX_RECV_ERRORS = 10;
- public WifiMonitor(WifiStateTracker tracker) {
- mWifiStateTracker = tracker;
+ public WifiMonitor(WifiStateMachine wifiStateMachine) {
+ mWifiStateMachine = wifiStateMachine;
}
public void startMonitoring() {
new MonitorThread().start();
}
- public NetworkStateTracker getNetworkStateTracker() {
- return mWifiStateTracker;
- }
-
class MonitorThread extends Thread {
public MonitorThread() {
super("WifiMonitor");
@@ -161,9 +156,9 @@
if (connectToSupplicant()) {
// Send a message indicating that it is now possible to send commands
// to the supplicant
- mWifiStateTracker.notifySupplicantConnection();
+ mWifiStateMachine.notifySupplicantConnection();
} else {
- mWifiStateTracker.notifySupplicantLost();
+ mWifiStateMachine.notifySupplicantLost();
return;
}
@@ -259,7 +254,7 @@
}
// notify and exit
- mWifiStateTracker.notifySupplicantLost();
+ mWifiStateMachine.notifySupplicantLost();
break;
} else {
handleEvent(event, eventData);
@@ -285,7 +280,7 @@
}
private void handlePasswordKeyMayBeIncorrect() {
- mWifiStateTracker.notifyPasswordKeyMayBeIncorrect();
+ mWifiStateMachine.notifyPasswordKeyMayBeIncorrect();
}
private void handleDriverEvent(String state) {
@@ -293,11 +288,11 @@
return;
}
if (state.equals("STOPPED")) {
- mWifiStateTracker.notifyDriverStopped();
+ mWifiStateMachine.notifyDriverStopped();
} else if (state.equals("STARTED")) {
- mWifiStateTracker.notifyDriverStarted();
+ mWifiStateMachine.notifyDriverStarted();
} else if (state.equals("HANGED")) {
- mWifiStateTracker.notifyDriverHung();
+ mWifiStateMachine.notifyDriverHung();
}
}
@@ -318,7 +313,7 @@
break;
case SCAN_RESULTS:
- mWifiStateTracker.notifyScanResultsAvailable();
+ mWifiStateMachine.notifyScanResultsAvailable();
break;
case UNKNOWN:
@@ -375,7 +370,7 @@
if (newSupplicantState == SupplicantState.INVALID) {
Log.w(TAG, "Invalid supplicant state: " + newState);
}
- mWifiStateTracker.notifySupplicantStateChange(networkId, BSSID, newSupplicantState);
+ mWifiStateMachine.notifySupplicantStateChange(networkId, BSSID, newSupplicantState);
}
}
@@ -395,7 +390,7 @@
}
}
}
- mWifiStateTracker.notifyNetworkStateChange(newState, BSSID, networkId);
+ mWifiStateMachine.notifyNetworkStateChange(newState, BSSID, networkId);
}
/**
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
new file mode 100644
index 0000000..845508b
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -0,0 +1,3571 @@
+/*
+ * 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.wifi;
+
+import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
+import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
+import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
+import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
+import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
+
+/**
+ * TODO: Add soft AP states as part of WIFI_STATE_XXX
+ * Retain WIFI_STATE_ENABLING that indicates driver is loading
+ * Add WIFI_STATE_AP_ENABLED to indicate soft AP has started
+ * and WIFI_STATE_FAILED for failure
+ * Deprecate WIFI_STATE_UNKNOWN
+ *
+ * Doing this will simplify the logic for sending broadcasts
+ */
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
+
+import android.app.ActivityManagerNative;
+import android.net.NetworkInfo;
+import android.net.DhcpInfo;
+import android.net.NetworkUtils;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkProperties;
+import android.os.Binder;
+import android.os.Message;
+import android.os.Parcelable;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.PowerManager;
+import android.os.SystemProperties;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.Process;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Slog;
+import android.app.backup.IBackupManager;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothA2dp;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.Context;
+import android.database.ContentObserver;
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.util.HierarchicalState;
+import com.android.internal.util.HierarchicalStateMachine;
+
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Pattern;
+
+/**
+ * Track the state of Wifi connectivity. All event handling is done here,
+ * and all changes in connectivity state are initiated here.
+ *
+ * @hide
+ */
+//TODO: we still need frequent scanning for the case when
+// we issue disconnect but need scan results for open network notification
+public class WifiStateMachine extends HierarchicalStateMachine {
+
+ private static final String TAG = "WifiStateMachine";
+ private static final String NETWORKTYPE = "WIFI";
+ private static final boolean DBG = false;
+
+ /* TODO: fetch a configurable interface */
+ private static final String SOFTAP_IFACE = "wl0.1";
+
+ private WifiMonitor mWifiMonitor;
+ private INetworkManagementService nwService;
+ private ConnectivityManager mCm;
+
+ /* Scan results handling */
+ private List<ScanResult> mScanResults;
+ private static final Pattern scanResultPattern = Pattern.compile("\t+");
+ private static final int SCAN_RESULT_CACHE_SIZE = 80;
+ private final LinkedHashMap<String, ScanResult> mScanResultCache;
+
+ private String mInterfaceName;
+
+ private int mNumAllowedChannels = 0;
+ private int mLastSignalLevel = -1;
+ private String mLastBssid;
+ private int mLastNetworkId;
+ private boolean mEnableRssiPolling = false;
+ private boolean mPasswordKeyMayBeIncorrect = false;
+ private boolean mUseStaticIp = false;
+ private int mReconnectCount = 0;
+ private boolean mIsScanMode = false;
+ private boolean mConfigChanged = false;
+
+ /**
+ * Instance of the bluetooth headset helper. This needs to be created
+ * early because there is a delay before it actually 'connects', as
+ * noted by its javadoc. If we check before it is connected, it will be
+ * in an error state and we will not disable coexistence.
+ */
+ private BluetoothHeadset mBluetoothHeadset;
+
+ private BluetoothA2dp mBluetoothA2dp;
+
+ /**
+ * Observes the static IP address settings.
+ */
+ private SettingsObserver mSettingsObserver;
+ private NetworkProperties mNetworkProperties;
+
+ // Held during driver load and unload
+ private static PowerManager.WakeLock sWakeLock;
+
+ private Context mContext;
+
+ private DhcpInfo mDhcpInfo;
+ private WifiInfo mWifiInfo;
+ private NetworkInfo mNetworkInfo;
+ private SupplicantStateTracker mSupplicantStateTracker;
+
+ // Event log tags (must be in sync with event-log-tags)
+ private static final int EVENTLOG_WIFI_STATE_CHANGED = 50021;
+ private static final int EVENTLOG_WIFI_EVENT_HANDLED = 50022;
+ private static final int EVENTLOG_SUPPLICANT_STATE_CHANGED = 50023;
+
+ /* Load the driver */
+ private static final int CMD_LOAD_DRIVER = 1;
+ /* Unload the driver */
+ private static final int CMD_UNLOAD_DRIVER = 2;
+ /* Indicates driver load succeeded */
+ private static final int CMD_LOAD_DRIVER_SUCCESS = 3;
+ /* Indicates driver load failed */
+ private static final int CMD_LOAD_DRIVER_FAILURE = 4;
+ /* Indicates driver unload succeeded */
+ private static final int CMD_UNLOAD_DRIVER_SUCCESS = 5;
+ /* Indicates driver unload failed */
+ private static final int CMD_UNLOAD_DRIVER_FAILURE = 6;
+
+ /* Start the supplicant */
+ private static final int CMD_START_SUPPLICANT = 11;
+ /* Stop the supplicant */
+ private static final int CMD_STOP_SUPPLICANT = 12;
+ /* Start the driver */
+ private static final int CMD_START_DRIVER = 13;
+ /* Start the driver */
+ private static final int CMD_STOP_DRIVER = 14;
+ /* Indicates DHCP succeded */
+ private static final int CMD_IP_CONFIG_SUCCESS = 15;
+ /* Indicates DHCP failed */
+ private static final int CMD_IP_CONFIG_FAILURE = 16;
+ /* Re-configure interface */
+ private static final int CMD_RECONFIGURE_IP = 17;
+
+
+ /* Start the soft access point */
+ private static final int CMD_START_AP = 21;
+ /* Stop the soft access point */
+ private static final int CMD_STOP_AP = 22;
+
+
+ /* Supplicant events */
+ /* Connection to supplicant established */
+ private static final int SUP_CONNECTION_EVENT = 31;
+ /* Connection to supplicant lost */
+ private static final int SUP_DISCONNECTION_EVENT = 32;
+ /* Driver start completed */
+ private static final int DRIVER_START_EVENT = 33;
+ /* Driver stop completed */
+ private static final int DRIVER_STOP_EVENT = 34;
+ /* Network connection completed */
+ private static final int NETWORK_CONNECTION_EVENT = 36;
+ /* Network disconnection completed */
+ private static final int NETWORK_DISCONNECTION_EVENT = 37;
+ /* Scan results are available */
+ private static final int SCAN_RESULTS_EVENT = 38;
+ /* Supplicate state changed */
+ private static final int SUPPLICANT_STATE_CHANGE_EVENT = 39;
+ /* Password may be incorrect */
+ private static final int PASSWORD_MAY_BE_INCORRECT_EVENT = 40;
+
+ /* Supplicant commands */
+ /* Is supplicant alive ? */
+ private static final int CMD_PING_SUPPLICANT = 51;
+ /* Add/update a network configuration */
+ private static final int CMD_ADD_OR_UPDATE_NETWORK = 52;
+ /* Delete a network */
+ private static final int CMD_REMOVE_NETWORK = 53;
+ /* Enable a network. The device will attempt a connection to the given network. */
+ private static final int CMD_ENABLE_NETWORK = 54;
+ /* Disable a network. The device does not attempt a connection to the given network. */
+ private static final int CMD_DISABLE_NETWORK = 55;
+ /* Blacklist network. De-prioritizes the given BSSID for connection. */
+ private static final int CMD_BLACKLIST_NETWORK = 56;
+ /* Clear the blacklist network list */
+ private static final int CMD_CLEAR_BLACKLIST = 57;
+ /* Get the configured networks */
+ private static final int CMD_GET_NETWORK_CONFIG = 58;
+ /* Save configuration */
+ private static final int CMD_SAVE_CONFIG = 59;
+ /* Connection status */
+ private static final int CMD_CONNECTION_STATUS = 60;
+
+ /* Supplicant commands after driver start*/
+ /* Initiate a scan */
+ private static final int CMD_START_SCAN = 71;
+ /* Set scan mode. CONNECT_MODE or SCAN_ONLY_MODE */
+ private static final int CMD_SET_SCAN_MODE = 72;
+ /* Set scan type. SCAN_ACTIVE or SCAN_PASSIVE */
+ private static final int CMD_SET_SCAN_TYPE = 73;
+ /* Disconnect from a network */
+ private static final int CMD_DISCONNECT = 74;
+ /* Reconnect to a network */
+ private static final int CMD_RECONNECT = 75;
+ /* Reassociate to a network */
+ private static final int CMD_REASSOCIATE = 76;
+ /* Set power mode
+ * POWER_MODE_ACTIVE
+ * POWER_MODE_AUTO
+ */
+ private static final int CMD_SET_POWER_MODE = 77;
+ /* Set bluetooth co-existence
+ * BLUETOOTH_COEXISTENCE_MODE_ENABLED
+ * BLUETOOTH_COEXISTENCE_MODE_DISABLED
+ * BLUETOOTH_COEXISTENCE_MODE_SENSE
+ */
+ private static final int CMD_SET_BLUETOOTH_COEXISTENCE = 78;
+ /* Enable/disable bluetooth scan mode
+ * true(1)
+ * false(0)
+ */
+ private static final int CMD_SET_BLUETOOTH_SCAN_MODE = 79;
+ /* Set number of allowed channels */
+ private static final int CMD_SET_NUM_ALLOWED_CHANNELS = 80;
+ /* Request connectivity manager wake lock before driver stop */
+ private static final int CMD_REQUEST_CM_WAKELOCK = 81;
+ /* Enables RSSI poll */
+ private static final int CMD_ENABLE_RSSI_POLL = 82;
+ /* RSSI poll */
+ private static final int CMD_RSSI_POLL = 83;
+ /* Get current RSSI */
+ private static final int CMD_GET_RSSI = 84;
+ /* Get approx current RSSI */
+ private static final int CMD_GET_RSSI_APPROX = 85;
+ /* Get link speed on connection */
+ private static final int CMD_GET_LINK_SPEED = 86;
+ /* Radio mac address */
+ private static final int CMD_GET_MAC_ADDR = 87;
+ /* Set up packet filtering */
+ private static final int CMD_START_PACKET_FILTERING = 88;
+ /* Clear packet filter */
+ private static final int CMD_STOP_PACKET_FILTERING = 89;
+
+ /**
+ * Interval in milliseconds between polling for connection
+ * status items that are not sent via asynchronous events.
+ * An example is RSSI (signal strength).
+ */
+ private static final int POLL_RSSI_INTERVAL_MSECS = 3000;
+
+ private static final int CONNECT_MODE = 1;
+ private static final int SCAN_ONLY_MODE = 2;
+
+ private static final int SCAN_ACTIVE = 1;
+ private static final int SCAN_PASSIVE = 2;
+
+ /**
+ * The maximum number of times we will retry a connection to an access point
+ * for which we have failed in acquiring an IP address from DHCP. A value of
+ * N means that we will make N+1 connection attempts in all.
+ * <p>
+ * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
+ * value if a Settings value is not present.
+ */
+ private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
+
+ private static final int DRIVER_POWER_MODE_ACTIVE = 1;
+ private static final int DRIVER_POWER_MODE_AUTO = 0;
+
+ /* Default parent state */
+ private HierarchicalState mDefaultState = new DefaultState();
+ /* Temporary initial state */
+ private HierarchicalState mInitialState = new InitialState();
+ /* Unloading the driver */
+ private HierarchicalState mDriverUnloadingState = new DriverUnloadingState();
+ /* Loading the driver */
+ private HierarchicalState mDriverUnloadedState = new DriverUnloadedState();
+ /* Driver load/unload failed */
+ private HierarchicalState mDriverFailedState = new DriverFailedState();
+ /* Driver loading */
+ private HierarchicalState mDriverLoadingState = new DriverLoadingState();
+ /* Driver loaded */
+ private HierarchicalState mDriverLoadedState = new DriverLoadedState();
+ /* Driver loaded, waiting for supplicant to start */
+ private HierarchicalState mWaitForSupState = new WaitForSupState();
+
+ /* Driver loaded and supplicant ready */
+ private HierarchicalState mDriverSupReadyState = new DriverSupReadyState();
+ /* Driver start issued, waiting for completed event */
+ private HierarchicalState mDriverStartingState = new DriverStartingState();
+ /* Driver started */
+ private HierarchicalState mDriverStartedState = new DriverStartedState();
+ /* Driver stopping */
+ private HierarchicalState mDriverStoppingState = new DriverStoppingState();
+ /* Driver stopped */
+ private HierarchicalState mDriverStoppedState = new DriverStoppedState();
+ /* Scan for networks, no connection will be established */
+ private HierarchicalState mScanModeState = new ScanModeState();
+ /* Connecting to an access point */
+ private HierarchicalState mConnectModeState = new ConnectModeState();
+ /* Fetching IP after network connection (assoc+auth complete) */
+ private HierarchicalState mConnectingState = new ConnectingState();
+ /* Connected with IP addr */
+ private HierarchicalState mConnectedState = new ConnectedState();
+ /* disconnect issued, waiting for network disconnect confirmation */
+ private HierarchicalState mDisconnectingState = new DisconnectingState();
+ /* Network is not connected, supplicant assoc+auth is not complete */
+ private HierarchicalState mDisconnectedState = new DisconnectedState();
+
+ /* Soft Ap is running */
+ private HierarchicalState mSoftApStartedState = new SoftApStartedState();
+
+ /* Argument for Message object to indicate a synchronous call */
+ private static final int SYNCHRONOUS_CALL = 1;
+ private static final int ASYNCHRONOUS_CALL = 0;
+
+
+ /**
+ * One of {@link WifiManager#WIFI_STATE_DISABLED},
+ * {@link WifiManager#WIFI_STATE_DISABLING},
+ * {@link WifiManager#WIFI_STATE_ENABLED},
+ * {@link WifiManager#WIFI_STATE_ENABLING},
+ * {@link WifiManager#WIFI_STATE_UNKNOWN}
+ *
+ */
+ private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_DISABLED);
+
+ /**
+ * One of {@link WifiManager#WIFI_AP_STATE_DISABLED},
+ * {@link WifiManager#WIFI_AP_STATE_DISABLING},
+ * {@link WifiManager#WIFI_AP_STATE_ENABLED},
+ * {@link WifiManager#WIFI_AP_STATE_ENABLING},
+ * {@link WifiManager#WIFI_AP_STATE_FAILED}
+ *
+ */
+ private final AtomicInteger mWifiApState = new AtomicInteger(WIFI_AP_STATE_DISABLED);
+
+ private final AtomicInteger mLastEnableUid = new AtomicInteger(Process.myUid());
+ private final AtomicInteger mLastApEnableUid = new AtomicInteger(Process.myUid());
+
+ private final IBatteryStats mBatteryStats;
+
+ public WifiStateMachine(Context context) {
+ super(TAG);
+
+ mContext = context;
+
+ mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
+ mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
+
+ IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ nwService = INetworkManagementService.Stub.asInterface(b);
+
+ mWifiMonitor = new WifiMonitor(this);
+ mDhcpInfo = new DhcpInfo();
+ mWifiInfo = new WifiInfo();
+ mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0");
+ mSupplicantStateTracker = new SupplicantStateTracker(context, getHandler());
+
+ mBluetoothHeadset = new BluetoothHeadset(mContext, null);
+ mNetworkProperties = new NetworkProperties();
+
+ mNetworkInfo.setIsAvailable(false);
+ mNetworkProperties.clear();
+ mLastBssid = null;
+ mLastNetworkId = -1;
+ mLastSignalLevel = -1;
+
+ mScanResultCache = new LinkedHashMap<String, ScanResult>(
+ SCAN_RESULT_CACHE_SIZE, 0.75f, true) {
+ /*
+ * Limit the cache size by SCAN_RESULT_CACHE_SIZE
+ * elements
+ */
+ @Override
+ public boolean removeEldestEntry(Map.Entry eldest) {
+ return SCAN_RESULT_CACHE_SIZE < this.size();
+ }
+ };
+
+ mSettingsObserver = new SettingsObserver(new Handler());
+
+ PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+ sWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+
+ addState(mDefaultState);
+ addState(mInitialState, mDefaultState);
+ addState(mDriverUnloadingState, mDefaultState);
+ addState(mDriverUnloadedState, mDefaultState);
+ addState(mDriverFailedState, mDriverUnloadedState);
+ addState(mDriverLoadingState, mDefaultState);
+ addState(mDriverLoadedState, mDefaultState);
+ addState(mWaitForSupState, mDriverLoadedState);
+ addState(mDriverSupReadyState, mDefaultState);
+ addState(mDriverStartingState, mDriverSupReadyState);
+ addState(mDriverStartedState, mDriverSupReadyState);
+ addState(mScanModeState, mDriverStartedState);
+ addState(mConnectModeState, mDriverStartedState);
+ addState(mConnectingState, mConnectModeState);
+ addState(mConnectedState, mConnectModeState);
+ addState(mDisconnectingState, mConnectModeState);
+ addState(mDisconnectedState, mConnectModeState);
+ addState(mDriverStoppingState, mDriverSupReadyState);
+ addState(mDriverStoppedState, mDriverSupReadyState);
+ addState(mSoftApStartedState, mDefaultState);
+
+ setInitialState(mInitialState);
+
+ if (DBG) setDbg(true);
+
+ //start the state machine
+ start();
+ }
+
+ /*********************************************************
+ * Methods exposed for public use
+ ********************************************************/
+
+ /**
+ * TODO: doc
+ */
+ public boolean pingSupplicant() {
+ return sendSyncMessage(CMD_PING_SUPPLICANT).boolValue;
+ }
+
+ /**
+ * TODO: doc
+ */
+ public boolean startScan(boolean forceActive) {
+ return sendSyncMessage(obtainMessage(CMD_START_SCAN, forceActive ?
+ SCAN_ACTIVE : SCAN_PASSIVE, 0)).boolValue;
+ }
+
+ /**
+ * TODO: doc
+ */
+ public void setWifiEnabled(boolean enable) {
+ mLastEnableUid.set(Binder.getCallingUid());
+ if (enable) {
+ /* Argument is the state that is entered prior to load */
+ sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_STATE_ENABLING, 0));
+ sendMessage(CMD_START_SUPPLICANT);
+ } else {
+ sendMessage(CMD_STOP_SUPPLICANT);
+ /* Argument is the state that is entered upon success */
+ sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_DISABLED, 0));
+ }
+ }
+
+ /**
+ * TODO: doc
+ */
+ public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enable) {
+ mLastApEnableUid.set(Binder.getCallingUid());
+ if (enable) {
+ /* Argument is the state that is entered prior to load */
+ sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_AP_STATE_ENABLING, 0));
+ sendMessage(obtainMessage(CMD_START_AP, wifiConfig));
+ } else {
+ sendMessage(CMD_STOP_AP);
+ /* Argument is the state that is entered upon success */
+ sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_DISABLED, 0));
+ }
+ }
+
+ /**
+ * TODO: doc
+ */
+ public int getWifiState() {
+ return mWifiState.get();
+ }
+
+ /**
+ * TODO: doc
+ */
+ public String getWifiStateByName() {
+ switch (mWifiState.get()) {
+ case WIFI_STATE_DISABLING:
+ return "disabling";
+ case WIFI_STATE_DISABLED:
+ return "disabled";
+ case WIFI_STATE_ENABLING:
+ return "enabling";
+ case WIFI_STATE_ENABLED:
+ return "enabled";
+ case WIFI_STATE_UNKNOWN:
+ return "unknown state";
+ default:
+ return "[invalid state]";
+ }
+ }
+
+ /**
+ * TODO: doc
+ */
+ public int getWifiApState() {
+ return mWifiApState.get();
+ }
+
+ /**
+ * TODO: doc
+ */
+ public String getWifiApStateByName() {
+ switch (mWifiApState.get()) {
+ case WIFI_AP_STATE_DISABLING:
+ return "disabling";
+ case WIFI_AP_STATE_DISABLED:
+ return "disabled";
+ case WIFI_AP_STATE_ENABLING:
+ return "enabling";
+ case WIFI_AP_STATE_ENABLED:
+ return "enabled";
+ case WIFI_AP_STATE_FAILED:
+ return "failed";
+ default:
+ return "[invalid state]";
+ }
+ }
+
+ /**
+ * Get status information for the current connection, if any.
+ * @return a {@link WifiInfo} object containing information about the current connection
+ *
+ */
+ public WifiInfo requestConnectionInfo() {
+ return mWifiInfo;
+ }
+
+ public DhcpInfo getDhcpInfo() {
+ return mDhcpInfo;
+ }
+
+ /**
+ * TODO: doc
+ */
+ public void setDriverStart(boolean enable) {
+ if (enable) {
+ sendMessage(CMD_START_DRIVER);
+ } else {
+ sendMessage(CMD_STOP_DRIVER);
+ }
+ }
+
+ /**
+ * TODO: doc
+ */
+ public void setScanOnlyMode(boolean enable) {
+ if (enable) {
+ sendMessage(obtainMessage(CMD_SET_SCAN_MODE, SCAN_ONLY_MODE, 0));
+ } else {
+ sendMessage(obtainMessage(CMD_SET_SCAN_MODE, CONNECT_MODE, 0));
+ }
+ }
+
+ /**
+ * TODO: doc
+ */
+ public void setScanType(boolean active) {
+ if (active) {
+ sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_ACTIVE, 0));
+ } else {
+ sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_PASSIVE, 0));
+ }
+ }
+
+ /**
+ * TODO: doc
+ */
+ public List<ScanResult> getScanResultsList() {
+ return mScanResults;
+ }
+
+ /**
+ * Disconnect from Access Point
+ */
+ public boolean disconnectCommand() {
+ return sendSyncMessage(CMD_DISCONNECT).boolValue;
+ }
+
+ /**
+ * Initiate a reconnection to AP
+ */
+ public boolean reconnectCommand() {
+ return sendSyncMessage(CMD_RECONNECT).boolValue;
+ }
+
+ /**
+ * Initiate a re-association to AP
+ */
+ public boolean reassociateCommand() {
+ return sendSyncMessage(CMD_REASSOCIATE).boolValue;
+ }
+
+ /**
+ * Add a network synchronously
+ *
+ * @return network id of the new network
+ */
+ public int addOrUpdateNetwork(WifiConfiguration config) {
+ return sendSyncMessage(CMD_ADD_OR_UPDATE_NETWORK, config).intValue;
+ }
+
+ public List<WifiConfiguration> getConfiguredNetworks() {
+ return sendSyncMessage(CMD_GET_NETWORK_CONFIG).configList;
+ }
+
+ /**
+ * Delete a network
+ *
+ * @param networkId id of the network to be removed
+ */
+ public boolean removeNetwork(int networkId) {
+ return sendSyncMessage(obtainMessage(CMD_REMOVE_NETWORK, networkId, 0)).boolValue;
+ }
+
+ private class EnableNetParams {
+ private int netId;
+ private boolean disableOthers;
+ EnableNetParams(int n, boolean b) {
+ netId = n;
+ disableOthers = b;
+ }
+ }
+ /**
+ * Enable a network
+ *
+ * @param netId network id of the network
+ * @param disableOthers true, if all other networks have to be disabled
+ * @return {@code true} if the operation succeeds, {@code false} otherwise
+ */
+ public boolean enableNetwork(int netId, boolean disableOthers) {
+ return sendSyncMessage(CMD_ENABLE_NETWORK,
+ new EnableNetParams(netId, disableOthers)).boolValue;
+ }
+
+ /**
+ * Disable a network
+ *
+ * @param netId network id of the network
+ * @return {@code true} if the operation succeeds, {@code false} otherwise
+ */
+ public boolean disableNetwork(int netId) {
+ return sendSyncMessage(obtainMessage(CMD_DISABLE_NETWORK, netId, 0)).boolValue;
+ }
+
+ /**
+ * Blacklist a BSSID. This will avoid the AP if there are
+ * alternate APs to connect
+ *
+ * @param bssid BSSID of the network
+ */
+ public void addToBlacklist(String bssid) {
+ sendMessage(obtainMessage(CMD_BLACKLIST_NETWORK, bssid));
+ }
+
+ /**
+ * Clear the blacklist list
+ *
+ */
+ public void clearBlacklist() {
+ sendMessage(obtainMessage(CMD_CLEAR_BLACKLIST));
+ }
+
+ /**
+ * Get detailed status of the connection
+ *
+ * @return Example status result
+ * bssid=aa:bb:cc:dd:ee:ff
+ * ssid=TestNet
+ * id=3
+ * pairwise_cipher=NONE
+ * group_cipher=NONE
+ * key_mgmt=NONE
+ * wpa_state=COMPLETED
+ * ip_address=X.X.X.X
+ */
+ public String status() {
+ return sendSyncMessage(CMD_CONNECTION_STATUS).stringValue;
+ }
+
+ public void enableRssiPolling(boolean enabled) {
+ sendMessage(obtainMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0));
+ }
+ /**
+ * Get RSSI to currently connected network
+ *
+ * @return RSSI value, -1 on failure
+ */
+ public int getRssi() {
+ return sendSyncMessage(CMD_GET_RSSI).intValue;
+ }
+
+ /**
+ * Get approx RSSI to currently connected network
+ *
+ * @return RSSI value, -1 on failure
+ */
+ public int getRssiApprox() {
+ return sendSyncMessage(CMD_GET_RSSI_APPROX).intValue;
+ }
+
+ /**
+ * Get link speed to currently connected network
+ *
+ * @return link speed, -1 on failure
+ */
+ public int getLinkSpeed() {
+ return sendSyncMessage(CMD_GET_LINK_SPEED).intValue;
+ }
+
+ /**
+ * Get MAC address of radio
+ *
+ * @return MAC address, null on failure
+ */
+ public String getMacAddress() {
+ return sendSyncMessage(CMD_GET_MAC_ADDR).stringValue;
+ }
+
+ /**
+ * Start packet filtering
+ */
+ public void startPacketFiltering() {
+ sendMessage(CMD_START_PACKET_FILTERING);
+ }
+
+ /**
+ * Stop packet filtering
+ */
+ public void stopPacketFiltering() {
+ sendMessage(CMD_STOP_PACKET_FILTERING);
+ }
+
+ /**
+ * Set power mode
+ * @param mode
+ * DRIVER_POWER_MODE_AUTO
+ * DRIVER_POWER_MODE_ACTIVE
+ */
+ public void setPowerMode(int mode) {
+ sendMessage(obtainMessage(CMD_SET_POWER_MODE, mode, 0));
+ }
+
+ /**
+ * Set the number of allowed radio frequency channels from the system
+ * setting value, if any.
+ */
+ public void setNumAllowedChannels() {
+ try {
+ setNumAllowedChannels(
+ Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS));
+ } catch (Settings.SettingNotFoundException e) {
+ if (mNumAllowedChannels != 0) {
+ setNumAllowedChannels(mNumAllowedChannels);
+ }
+ // otherwise, use the driver default
+ }
+ }
+
+ /**
+ * Set the number of radio frequency channels that are allowed to be used
+ * in the current regulatory domain.
+ * @param numChannels the number of allowed channels. Must be greater than 0
+ * and less than or equal to 16.
+ */
+ public void setNumAllowedChannels(int numChannels) {
+ sendMessage(obtainMessage(CMD_SET_NUM_ALLOWED_CHANNELS, numChannels, 0));
+ }
+
+ /**
+ * Get number of allowed channels
+ *
+ * @return channel count, -1 on failure
+ *
+ * TODO: this is not a public API and needs to be removed in favor
+ * of asynchronous reporting. unused for now.
+ */
+ public int getNumAllowedChannels() {
+ return -1;
+ }
+
+ /**
+ * Set bluetooth coex mode:
+ *
+ * @param mode
+ * BLUETOOTH_COEXISTENCE_MODE_ENABLED
+ * BLUETOOTH_COEXISTENCE_MODE_DISABLED
+ * BLUETOOTH_COEXISTENCE_MODE_SENSE
+ */
+ public void setBluetoothCoexistenceMode(int mode) {
+ sendMessage(obtainMessage(CMD_SET_BLUETOOTH_COEXISTENCE, mode, 0));
+ }
+
+ /**
+ * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
+ * some of the low-level scan parameters used by the driver are changed to
+ * reduce interference with A2DP streaming.
+ *
+ * @param isBluetoothPlaying whether to enable or disable this mode
+ */
+ public void setBluetoothScanMode(boolean isBluetoothPlaying) {
+ sendMessage(obtainMessage(CMD_SET_BLUETOOTH_SCAN_MODE, isBluetoothPlaying ? 1 : 0, 0));
+ }
+
+ /**
+ * Save configuration on supplicant
+ *
+ * @return {@code true} if the operation succeeds, {@code false} otherwise
+ *
+ * TODO: deprecate this
+ */
+ public boolean saveConfig() {
+ return sendSyncMessage(CMD_SAVE_CONFIG).boolValue;
+ }
+
+ /**
+ * TODO: doc
+ */
+ public void requestCmWakeLock() {
+ sendMessage(CMD_REQUEST_CM_WAKELOCK);
+ }
+
+ /*********************************************************
+ * Internal private functions
+ ********************************************************/
+
+ class SyncReturn {
+ boolean boolValue;
+ int intValue;
+ String stringValue;
+ Object objValue;
+ List<WifiConfiguration> configList;
+ }
+
+ class SyncParams {
+ Object mParameter;
+ SyncReturn mSyncReturn;
+ SyncParams() {
+ mSyncReturn = new SyncReturn();
+ }
+ SyncParams(Object p) {
+ mParameter = p;
+ mSyncReturn = new SyncReturn();
+ }
+ }
+
+ /**
+ * message.arg2 is reserved to indicate synchronized
+ * message.obj is used to store SyncParams
+ */
+ private SyncReturn syncedSend(Message msg) {
+ SyncParams syncParams = (SyncParams) msg.obj;
+ msg.arg2 = SYNCHRONOUS_CALL;
+ synchronized(syncParams) {
+ if (DBG) Log.d(TAG, "syncedSend " + msg);
+ sendMessage(msg);
+ try {
+ syncParams.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "sendSyncMessage: unexpected interruption of wait()");
+ return null;
+ }
+ }
+ return syncParams.mSyncReturn;
+ }
+
+ private SyncReturn sendSyncMessage(Message msg) {
+ SyncParams syncParams = new SyncParams();
+ msg.obj = syncParams;
+ return syncedSend(msg);
+ }
+
+ private SyncReturn sendSyncMessage(int what, Object param) {
+ SyncParams syncParams = new SyncParams(param);
+ Message msg = obtainMessage(what, syncParams);
+ return syncedSend(msg);
+ }
+
+
+ private SyncReturn sendSyncMessage(int what) {
+ return sendSyncMessage(obtainMessage(what));
+ }
+
+ private void notifyOnMsgObject(Message msg) {
+ SyncParams syncParams = (SyncParams) msg.obj;
+ if (syncParams != null) {
+ synchronized(syncParams) {
+ if (DBG) Log.d(TAG, "notifyOnMsgObject " + msg);
+ syncParams.notify();
+ }
+ }
+ else {
+ Log.e(TAG, "Error! syncParams in notifyOnMsgObject is null");
+ }
+ }
+
+ private void setWifiState(int wifiState) {
+ final int previousWifiState = mWifiState.get();
+
+ try {
+ if (wifiState == WIFI_STATE_ENABLED) {
+ mBatteryStats.noteWifiOn(mLastEnableUid.get());
+ } else if (wifiState == WIFI_STATE_DISABLED) {
+ mBatteryStats.noteWifiOff(mLastEnableUid.get());
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to note battery stats in wifi");
+ }
+
+ mWifiState.set(wifiState);
+
+ if (DBG) Log.d(TAG, "setWifiState: " + getWifiStateByName());
+
+ final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
+ intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
+ mContext.sendStickyBroadcast(intent);
+ }
+
+ private void setWifiApState(int wifiApState) {
+ final int previousWifiApState = mWifiApState.get();
+
+ try {
+ if (wifiApState == WIFI_AP_STATE_ENABLED) {
+ mBatteryStats.noteWifiOn(mLastApEnableUid.get());
+ } else if (wifiApState == WIFI_AP_STATE_DISABLED) {
+ mBatteryStats.noteWifiOff(mLastApEnableUid.get());
+ }
+ } catch (RemoteException e) {
+ Log.d(TAG, "Failed to note battery stats in wifi");
+ }
+
+ // Update state
+ mWifiApState.set(wifiApState);
+
+ if (DBG) Log.d(TAG, "setWifiApState: " + getWifiApStateByName());
+
+ final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState);
+ intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
+ mContext.sendStickyBroadcast(intent);
+ }
+
+ /**
+ * Parse the scan result line passed to us by wpa_supplicant (helper).
+ * @param line the line to parse
+ * @return the {@link ScanResult} object
+ */
+ private ScanResult parseScanResult(String line) {
+ ScanResult scanResult = null;
+ if (line != null) {
+ /*
+ * Cache implementation (LinkedHashMap) is not synchronized, thus,
+ * must synchronized here!
+ */
+ synchronized (mScanResultCache) {
+ String[] result = scanResultPattern.split(line);
+ if (3 <= result.length && result.length <= 5) {
+ String bssid = result[0];
+ // bssid | frequency | level | flags | ssid
+ int frequency;
+ int level;
+ try {
+ frequency = Integer.parseInt(result[1]);
+ level = Integer.parseInt(result[2]);
+ /* some implementations avoid negative values by adding 256
+ * so we need to adjust for that here.
+ */
+ if (level > 0) level -= 256;
+ } catch (NumberFormatException e) {
+ frequency = 0;
+ level = 0;
+ }
+
+ /*
+ * The formatting of the results returned by
+ * wpa_supplicant is intended to make the fields
+ * line up nicely when printed,
+ * not to make them easy to parse. So we have to
+ * apply some heuristics to figure out which field
+ * is the SSID and which field is the flags.
+ */
+ String ssid;
+ String flags;
+ if (result.length == 4) {
+ if (result[3].charAt(0) == '[') {
+ flags = result[3];
+ ssid = "";
+ } else {
+ flags = "";
+ ssid = result[3];
+ }
+ } else if (result.length == 5) {
+ flags = result[3];
+ ssid = result[4];
+ } else {
+ // Here, we must have 3 fields: no flags and ssid
+ // set
+ flags = "";
+ ssid = "";
+ }
+
+ // bssid + ssid is the hash key
+ String key = bssid + ssid;
+ scanResult = mScanResultCache.get(key);
+ if (scanResult != null) {
+ scanResult.level = level;
+ scanResult.SSID = ssid;
+ scanResult.capabilities = flags;
+ scanResult.frequency = frequency;
+ } else {
+ // Do not add scan results that have no SSID set
+ if (0 < ssid.trim().length()) {
+ scanResult =
+ new ScanResult(
+ ssid, bssid, flags, level, frequency);
+ mScanResultCache.put(key, scanResult);
+ }
+ }
+ } else {
+ Log.w(TAG, "Misformatted scan result text with " +
+ result.length + " fields: " + line);
+ }
+ }
+ }
+
+ return scanResult;
+ }
+
+ /**
+ * scanResults input format
+ * 00:bb:cc:dd:cc:ee 2427 166 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net1
+ * 00:bb:cc:dd:cc:ff 2412 165 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net2
+ */
+ private void setScanResults(String scanResults) {
+ if (scanResults == null) {
+ return;
+ }
+
+ List<ScanResult> scanList = new ArrayList<ScanResult>();
+
+ int lineCount = 0;
+
+ int scanResultsLen = scanResults.length();
+ // Parse the result string, keeping in mind that the last line does
+ // not end with a newline.
+ for (int lineBeg = 0, lineEnd = 0; lineEnd <= scanResultsLen; ++lineEnd) {
+ if (lineEnd == scanResultsLen || scanResults.charAt(lineEnd) == '\n') {
+ ++lineCount;
+
+ if (lineCount == 1) {
+ lineBeg = lineEnd + 1;
+ continue;
+ }
+ if (lineEnd > lineBeg) {
+ String line = scanResults.substring(lineBeg, lineEnd);
+ ScanResult scanResult = parseScanResult(line);
+ if (scanResult != null) {
+ scanList.add(scanResult);
+ } else {
+ Log.w(TAG, "misformatted scan result for: " + line);
+ }
+ }
+ lineBeg = lineEnd + 1;
+ }
+ }
+
+ mScanResults = scanList;
+ }
+
+ private void configureNetworkProperties() {
+ try {
+ mNetworkProperties.setInterface(NetworkInterface.getByName(mInterfaceName));
+ } catch (SocketException e) {
+ Log.e(TAG, "SocketException creating NetworkInterface from " + mInterfaceName +
+ ". e=" + e);
+ return;
+ } catch (NullPointerException e) {
+ Log.e(TAG, "NPE creating NetworkInterface. e=" + e);
+ return;
+ }
+ // TODO - fix this for v6
+ try {
+ mNetworkProperties.addAddress(InetAddress.getByAddress(
+ NetworkUtils.v4IntToArray(mDhcpInfo.ipAddress)));
+ } catch (UnknownHostException e) {
+ Log.e(TAG, "Exception setting IpAddress using " + mDhcpInfo + ", e=" + e);
+ }
+
+ try {
+ mNetworkProperties.setGateway(InetAddress.getByAddress(NetworkUtils.v4IntToArray(
+ mDhcpInfo.gateway)));
+ } catch (UnknownHostException e) {
+ Log.e(TAG, "Exception setting Gateway using " + mDhcpInfo + ", e=" + e);
+ }
+
+ try {
+ mNetworkProperties.addDns(InetAddress.getByAddress(
+ NetworkUtils.v4IntToArray(mDhcpInfo.dns1)));
+ } catch (UnknownHostException e) {
+ Log.e(TAG, "Exception setting Dns1 using " + mDhcpInfo + ", e=" + e);
+ }
+ try {
+ mNetworkProperties.addDns(InetAddress.getByAddress(
+ NetworkUtils.v4IntToArray(mDhcpInfo.dns2)));
+
+ } catch (UnknownHostException e) {
+ Log.e(TAG, "Exception setting Dns2 using " + mDhcpInfo + ", e=" + e);
+ }
+ // TODO - add proxy info
+ }
+
+
+ private void checkUseStaticIp() {
+ mUseStaticIp = false;
+ final ContentResolver cr = mContext.getContentResolver();
+ try {
+ if (Settings.System.getInt(cr, Settings.System.WIFI_USE_STATIC_IP) == 0) {
+ return;
+ }
+ } catch (Settings.SettingNotFoundException e) {
+ return;
+ }
+
+ try {
+ String addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_IP);
+ if (addr != null) {
+ mDhcpInfo.ipAddress = stringToIpAddr(addr);
+ } else {
+ return;
+ }
+ addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_GATEWAY);
+ if (addr != null) {
+ mDhcpInfo.gateway = stringToIpAddr(addr);
+ } else {
+ return;
+ }
+ addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_NETMASK);
+ if (addr != null) {
+ mDhcpInfo.netmask = stringToIpAddr(addr);
+ } else {
+ return;
+ }
+ addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS1);
+ if (addr != null) {
+ mDhcpInfo.dns1 = stringToIpAddr(addr);
+ } else {
+ return;
+ }
+ addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS2);
+ if (addr != null) {
+ mDhcpInfo.dns2 = stringToIpAddr(addr);
+ } else {
+ mDhcpInfo.dns2 = 0;
+ }
+ } catch (UnknownHostException e) {
+ return;
+ }
+ mUseStaticIp = true;
+ }
+
+ private static int stringToIpAddr(String addrString) throws UnknownHostException {
+ try {
+ String[] parts = addrString.split("\\.");
+ if (parts.length != 4) {
+ throw new UnknownHostException(addrString);
+ }
+
+ int a = Integer.parseInt(parts[0]) ;
+ int b = Integer.parseInt(parts[1]) << 8;
+ int c = Integer.parseInt(parts[2]) << 16;
+ int d = Integer.parseInt(parts[3]) << 24;
+
+ return a | b | c | d;
+ } catch (NumberFormatException ex) {
+ throw new UnknownHostException(addrString);
+ }
+ }
+
+ private int getMaxDhcpRetries() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT,
+ DEFAULT_MAX_DHCP_RETRIES);
+ }
+
+ private class SettingsObserver extends ContentObserver {
+ public SettingsObserver(Handler handler) {
+ super(handler);
+ ContentResolver cr = mContext.getContentResolver();
+ cr.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.WIFI_USE_STATIC_IP), false, this);
+ cr.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.WIFI_STATIC_IP), false, this);
+ cr.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.WIFI_STATIC_GATEWAY), false, this);
+ cr.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.WIFI_STATIC_NETMASK), false, this);
+ cr.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.WIFI_STATIC_DNS1), false, this);
+ cr.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.WIFI_STATIC_DNS2), false, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+
+ boolean wasStaticIp = mUseStaticIp;
+ int oIp, oGw, oMsk, oDns1, oDns2;
+ oIp = oGw = oMsk = oDns1 = oDns2 = 0;
+ if (wasStaticIp) {
+ oIp = mDhcpInfo.ipAddress;
+ oGw = mDhcpInfo.gateway;
+ oMsk = mDhcpInfo.netmask;
+ oDns1 = mDhcpInfo.dns1;
+ oDns2 = mDhcpInfo.dns2;
+ }
+ checkUseStaticIp();
+
+ if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) {
+ return;
+ }
+
+ boolean changed =
+ (wasStaticIp != mUseStaticIp) ||
+ (wasStaticIp && (
+ oIp != mDhcpInfo.ipAddress ||
+ oGw != mDhcpInfo.gateway ||
+ oMsk != mDhcpInfo.netmask ||
+ oDns1 != mDhcpInfo.dns1 ||
+ oDns2 != mDhcpInfo.dns2));
+
+ if (changed) {
+ sendMessage(CMD_RECONFIGURE_IP);
+ mConfigChanged = true;
+ }
+ }
+ }
+
+ /**
+ * Whether to disable coexistence mode while obtaining IP address. This
+ * logic will return true only if the current bluetooth
+ * headset/handsfree state is disconnected. This means if it is in an
+ * error state, we will NOT disable coexistence mode to err on the side
+ * of safety.
+ *
+ * @return Whether to disable coexistence mode.
+ */
+ private boolean shouldDisableCoexistenceMode() {
+ int state = mBluetoothHeadset.getState(mBluetoothHeadset.getCurrentHeadset());
+ return state == BluetoothHeadset.STATE_DISCONNECTED;
+ }
+
+ private void checkIsBluetoothPlaying() {
+ boolean isBluetoothPlaying = false;
+ Set<BluetoothDevice> connected = mBluetoothA2dp.getConnectedSinks();
+
+ for (BluetoothDevice device : connected) {
+ if (mBluetoothA2dp.getSinkState(device) == BluetoothA2dp.STATE_PLAYING) {
+ isBluetoothPlaying = true;
+ break;
+ }
+ }
+ setBluetoothScanMode(isBluetoothPlaying);
+ }
+
+ private void sendScanResultsAvailableBroadcast() {
+ if (!ActivityManagerNative.isSystemReady()) return;
+
+ mContext.sendBroadcast(new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
+ }
+
+ private void sendRssiChangeBroadcast(final int newRssi) {
+ if (!ActivityManagerNative.isSystemReady()) return;
+
+ Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
+ intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
+ mContext.sendBroadcast(intent);
+ }
+
+ private void sendNetworkStateChangeBroadcast(String bssid) {
+ Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+ | Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
+ intent.putExtra(WifiManager.EXTRA_NETWORK_PROPERTIES, mNetworkProperties);
+ if (bssid != null)
+ intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
+ mContext.sendStickyBroadcast(intent);
+ }
+
+ private void sendConfigChangeBroadcast() {
+ Intent intent = new Intent(WifiManager.CONFIG_CHANGED_ACTION);
+ intent.putExtra(WifiManager.EXTRA_NETWORK_PROPERTIES, mNetworkProperties);
+ mContext.sendBroadcast(intent);
+ }
+
+ private void sendSupplicantStateChangedBroadcast(StateChangeResult sc, boolean failedAuth) {
+ Intent intent = new Intent(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+ | Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra(WifiManager.EXTRA_NEW_STATE, (Parcelable)sc.state);
+ if (failedAuth) {
+ intent.putExtra(
+ WifiManager.EXTRA_SUPPLICANT_ERROR,
+ WifiManager.ERROR_AUTHENTICATING);
+ }
+ mContext.sendStickyBroadcast(intent);
+ }
+
+ private void sendSupplicantConnectionChangedBroadcast(boolean connected) {
+ if (!ActivityManagerNative.isSystemReady()) return;
+
+ Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
+ intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected);
+ mContext.sendBroadcast(intent);
+ }
+
+ /**
+ * Record the detailed state of a network.
+ * @param state the new @{code DetailedState}
+ */
+ private void setDetailedState(NetworkInfo.DetailedState state) {
+ Log.d(TAG, "setDetailed state, old ="
+ + mNetworkInfo.getDetailedState() + " and new state=" + state);
+ if (state != mNetworkInfo.getDetailedState()) {
+ mNetworkInfo.setDetailedState(state, null, null);
+ }
+ }
+
+ private static String removeDoubleQuotes(String string) {
+ if (string.length() <= 2) return "";
+ return string.substring(1, string.length() - 1);
+ }
+
+ private static String convertToQuotedString(String string) {
+ return "\"" + string + "\"";
+ }
+
+ private static String makeString(BitSet set, String[] strings) {
+ StringBuffer buf = new StringBuffer();
+ int nextSetBit = -1;
+
+ /* Make sure all set bits are in [0, strings.length) to avoid
+ * going out of bounds on strings. (Shouldn't happen, but...) */
+ set = set.get(0, strings.length);
+
+ while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
+ buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
+ }
+
+ // remove trailing space
+ if (set.cardinality() > 0) {
+ buf.setLength(buf.length() - 1);
+ }
+
+ return buf.toString();
+ }
+
+ private static int lookupString(String string, String[] strings) {
+ int size = strings.length;
+
+ string = string.replace('-', '_');
+
+ for (int i = 0; i < size; i++)
+ if (string.equals(strings[i]))
+ return i;
+
+ // if we ever get here, we should probably add the
+ // value to WifiConfiguration to reflect that it's
+ // supported by the WPA supplicant
+ Log.w(TAG, "Failed to look-up a string: " + string);
+
+ return -1;
+ }
+
+ private int addOrUpdateNetworkNative(WifiConfiguration config) {
+ /*
+ * If the supplied networkId is -1, we create a new empty
+ * network configuration. Otherwise, the networkId should
+ * refer to an existing configuration.
+ */
+ int netId = config.networkId;
+ boolean newNetwork = netId == -1;
+ // networkId of -1 means we want to create a new network
+
+ if (newNetwork) {
+ netId = WifiNative.addNetworkCommand();
+ if (netId < 0) {
+ Log.e(TAG, "Failed to add a network!");
+ return -1;
+ }
+ }
+
+ setVariables: {
+
+ if (config.SSID != null &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.ssidVarName,
+ config.SSID)) {
+ Log.d(TAG, "failed to set SSID: "+config.SSID);
+ break setVariables;
+ }
+
+ if (config.BSSID != null &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.bssidVarName,
+ config.BSSID)) {
+ Log.d(TAG, "failed to set BSSID: "+config.BSSID);
+ break setVariables;
+ }
+
+ String allowedKeyManagementString =
+ makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
+ if (config.allowedKeyManagement.cardinality() != 0 &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.KeyMgmt.varName,
+ allowedKeyManagementString)) {
+ Log.d(TAG, "failed to set key_mgmt: "+
+ allowedKeyManagementString);
+ break setVariables;
+ }
+
+ String allowedProtocolsString =
+ makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
+ if (config.allowedProtocols.cardinality() != 0 &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.Protocol.varName,
+ allowedProtocolsString)) {
+ Log.d(TAG, "failed to set proto: "+
+ allowedProtocolsString);
+ break setVariables;
+ }
+
+ String allowedAuthAlgorithmsString =
+ makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
+ if (config.allowedAuthAlgorithms.cardinality() != 0 &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.AuthAlgorithm.varName,
+ allowedAuthAlgorithmsString)) {
+ Log.d(TAG, "failed to set auth_alg: "+
+ allowedAuthAlgorithmsString);
+ break setVariables;
+ }
+
+ String allowedPairwiseCiphersString =
+ makeString(config.allowedPairwiseCiphers,
+ WifiConfiguration.PairwiseCipher.strings);
+ if (config.allowedPairwiseCiphers.cardinality() != 0 &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.PairwiseCipher.varName,
+ allowedPairwiseCiphersString)) {
+ Log.d(TAG, "failed to set pairwise: "+
+ allowedPairwiseCiphersString);
+ break setVariables;
+ }
+
+ String allowedGroupCiphersString =
+ makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
+ if (config.allowedGroupCiphers.cardinality() != 0 &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.GroupCipher.varName,
+ allowedGroupCiphersString)) {
+ Log.d(TAG, "failed to set group: "+
+ allowedGroupCiphersString);
+ break setVariables;
+ }
+
+ // Prevent client screw-up by passing in a WifiConfiguration we gave it
+ // by preventing "*" as a key.
+ if (config.preSharedKey != null && !config.preSharedKey.equals("*") &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.pskVarName,
+ config.preSharedKey)) {
+ Log.d(TAG, "failed to set psk: "+config.preSharedKey);
+ break setVariables;
+ }
+
+ boolean hasSetKey = false;
+ if (config.wepKeys != null) {
+ for (int i = 0; i < config.wepKeys.length; i++) {
+ // Prevent client screw-up by passing in a WifiConfiguration we gave it
+ // by preventing "*" as a key.
+ if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
+ if (!WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.wepKeyVarNames[i],
+ config.wepKeys[i])) {
+ Log.d(TAG,
+ "failed to set wep_key"+i+": " +
+ config.wepKeys[i]);
+ break setVariables;
+ }
+ hasSetKey = true;
+ }
+ }
+ }
+
+ if (hasSetKey) {
+ if (!WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.wepTxKeyIdxVarName,
+ Integer.toString(config.wepTxKeyIndex))) {
+ Log.d(TAG,
+ "failed to set wep_tx_keyidx: "+
+ config.wepTxKeyIndex);
+ break setVariables;
+ }
+ }
+
+ if (!WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.priorityVarName,
+ Integer.toString(config.priority))) {
+ Log.d(TAG, config.SSID + ": failed to set priority: "
+ +config.priority);
+ break setVariables;
+ }
+
+ if (config.hiddenSSID && !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.hiddenSSIDVarName,
+ Integer.toString(config.hiddenSSID ? 1 : 0))) {
+ Log.d(TAG, config.SSID + ": failed to set hiddenSSID: "+
+ config.hiddenSSID);
+ break setVariables;
+ }
+
+ for (WifiConfiguration.EnterpriseField field
+ : config.enterpriseFields) {
+ String varName = field.varName();
+ String value = field.value();
+ if (value != null) {
+ if (field != config.eap) {
+ value = (value.length() == 0) ? "NULL" : convertToQuotedString(value);
+ }
+ if (!WifiNative.setNetworkVariableCommand(
+ netId,
+ varName,
+ value)) {
+ Log.d(TAG, config.SSID + ": failed to set " + varName +
+ ": " + value);
+ break setVariables;
+ }
+ }
+ }
+ return netId;
+ }
+
+ if (newNetwork) {
+ WifiNative.removeNetworkCommand(netId);
+ Log.d(TAG,
+ "Failed to set a network variable, removed network: "
+ + netId);
+ }
+
+ return -1;
+ }
+
+ private List<WifiConfiguration> getConfiguredNetworksNative() {
+ String listStr = WifiNative.listNetworksCommand();
+
+ List<WifiConfiguration> networks =
+ new ArrayList<WifiConfiguration>();
+ if (listStr == null)
+ return networks;
+
+ String[] lines = listStr.split("\n");
+ // Skip the first line, which is a header
+ for (int i = 1; i < lines.length; i++) {
+ String[] result = lines[i].split("\t");
+ // network-id | ssid | bssid | flags
+ WifiConfiguration config = new WifiConfiguration();
+ try {
+ config.networkId = Integer.parseInt(result[0]);
+ } catch(NumberFormatException e) {
+ continue;
+ }
+ if (result.length > 3) {
+ if (result[3].indexOf("[CURRENT]") != -1)
+ config.status = WifiConfiguration.Status.CURRENT;
+ else if (result[3].indexOf("[DISABLED]") != -1)
+ config.status = WifiConfiguration.Status.DISABLED;
+ else
+ config.status = WifiConfiguration.Status.ENABLED;
+ } else {
+ config.status = WifiConfiguration.Status.ENABLED;
+ }
+ readNetworkVariables(config);
+ networks.add(config);
+ }
+ return networks;
+ }
+
+ /**
+ * Read the variables from the supplicant daemon that are needed to
+ * fill in the WifiConfiguration object.
+ *
+ * @param config the {@link WifiConfiguration} object to be filled in.
+ */
+ private void readNetworkVariables(WifiConfiguration config) {
+
+ int netId = config.networkId;
+ if (netId < 0)
+ return;
+
+ /*
+ * TODO: maybe should have a native method that takes an array of
+ * variable names and returns an array of values. But we'd still
+ * be doing a round trip to the supplicant daemon for each variable.
+ */
+ String value;
+
+ value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.ssidVarName);
+ if (!TextUtils.isEmpty(value)) {
+ config.SSID = removeDoubleQuotes(value);
+ } else {
+ config.SSID = null;
+ }
+
+ value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.bssidVarName);
+ if (!TextUtils.isEmpty(value)) {
+ config.BSSID = value;
+ } else {
+ config.BSSID = null;
+ }
+
+ value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName);
+ config.priority = -1;
+ if (!TextUtils.isEmpty(value)) {
+ try {
+ config.priority = Integer.parseInt(value);
+ } catch (NumberFormatException ignore) {
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.hiddenSSIDVarName);
+ config.hiddenSSID = false;
+ if (!TextUtils.isEmpty(value)) {
+ try {
+ config.hiddenSSID = Integer.parseInt(value) != 0;
+ } catch (NumberFormatException ignore) {
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepTxKeyIdxVarName);
+ config.wepTxKeyIndex = -1;
+ if (!TextUtils.isEmpty(value)) {
+ try {
+ config.wepTxKeyIndex = Integer.parseInt(value);
+ } catch (NumberFormatException ignore) {
+ }
+ }
+
+ for (int i = 0; i < 4; i++) {
+ value = WifiNative.getNetworkVariableCommand(netId,
+ WifiConfiguration.wepKeyVarNames[i]);
+ if (!TextUtils.isEmpty(value)) {
+ config.wepKeys[i] = value;
+ } else {
+ config.wepKeys[i] = null;
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.pskVarName);
+ if (!TextUtils.isEmpty(value)) {
+ config.preSharedKey = value;
+ } else {
+ config.preSharedKey = null;
+ }
+
+ value = WifiNative.getNetworkVariableCommand(config.networkId,
+ WifiConfiguration.Protocol.varName);
+ if (!TextUtils.isEmpty(value)) {
+ String vals[] = value.split(" ");
+ for (String val : vals) {
+ int index =
+ lookupString(val, WifiConfiguration.Protocol.strings);
+ if (0 <= index) {
+ config.allowedProtocols.set(index);
+ }
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(config.networkId,
+ WifiConfiguration.KeyMgmt.varName);
+ if (!TextUtils.isEmpty(value)) {
+ String vals[] = value.split(" ");
+ for (String val : vals) {
+ int index =
+ lookupString(val, WifiConfiguration.KeyMgmt.strings);
+ if (0 <= index) {
+ config.allowedKeyManagement.set(index);
+ }
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(config.networkId,
+ WifiConfiguration.AuthAlgorithm.varName);
+ if (!TextUtils.isEmpty(value)) {
+ String vals[] = value.split(" ");
+ for (String val : vals) {
+ int index =
+ lookupString(val, WifiConfiguration.AuthAlgorithm.strings);
+ if (0 <= index) {
+ config.allowedAuthAlgorithms.set(index);
+ }
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(config.networkId,
+ WifiConfiguration.PairwiseCipher.varName);
+ if (!TextUtils.isEmpty(value)) {
+ String vals[] = value.split(" ");
+ for (String val : vals) {
+ int index =
+ lookupString(val, WifiConfiguration.PairwiseCipher.strings);
+ if (0 <= index) {
+ config.allowedPairwiseCiphers.set(index);
+ }
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(config.networkId,
+ WifiConfiguration.GroupCipher.varName);
+ if (!TextUtils.isEmpty(value)) {
+ String vals[] = value.split(" ");
+ for (String val : vals) {
+ int index =
+ lookupString(val, WifiConfiguration.GroupCipher.strings);
+ if (0 <= index) {
+ config.allowedGroupCiphers.set(index);
+ }
+ }
+ }
+
+ for (WifiConfiguration.EnterpriseField field :
+ config.enterpriseFields) {
+ value = WifiNative.getNetworkVariableCommand(netId,
+ field.varName());
+ if (!TextUtils.isEmpty(value)) {
+ if (field != config.eap) value = removeDoubleQuotes(value);
+ field.setValue(value);
+ }
+ }
+
+ }
+
+ /**
+ * Poll for info not reported via events
+ * RSSI & Linkspeed
+ */
+ private void requestPolledInfo() {
+ int newRssi = WifiNative.getRssiCommand();
+ if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values
+ /* some implementations avoid negative values by adding 256
+ * so we need to adjust for that here.
+ */
+ if (newRssi > 0) newRssi -= 256;
+ mWifiInfo.setRssi(newRssi);
+ /*
+ * Rather then sending the raw RSSI out every time it
+ * changes, we precalculate the signal level that would
+ * be displayed in the status bar, and only send the
+ * broadcast if that much more coarse-grained number
+ * changes. This cuts down greatly on the number of
+ * broadcasts, at the cost of not mWifiInforming others
+ * interested in RSSI of all the changes in signal
+ * level.
+ */
+ // TODO: The second arg to the call below needs to be a symbol somewhere, but
+ // it's actually the size of an array of icons that's private
+ // to StatusBar Policy.
+ int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4);
+ if (newSignalLevel != mLastSignalLevel) {
+ sendRssiChangeBroadcast(newRssi);
+ }
+ mLastSignalLevel = newSignalLevel;
+ } else {
+ mWifiInfo.setRssi(-200);
+ }
+ int newLinkSpeed = WifiNative.getLinkSpeedCommand();
+ if (newLinkSpeed != -1) {
+ mWifiInfo.setLinkSpeed(newLinkSpeed);
+ }
+ }
+
+ /**
+ * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
+ * using the interface, stopping DHCP & disabling interface
+ */
+ private void handleNetworkDisconnect() {
+ Log.d(TAG, "Reset connections and stopping DHCP");
+
+ /*
+ * Reset connections & stop DHCP
+ */
+ NetworkUtils.resetConnections(mInterfaceName);
+
+ if (!NetworkUtils.stopDhcp(mInterfaceName)) {
+ Log.e(TAG, "Could not stop DHCP");
+ }
+
+ /* Disable interface */
+ NetworkUtils.disableInterface(mInterfaceName);
+
+ /* send event to CM & network change broadcast */
+ setDetailedState(DetailedState.DISCONNECTED);
+ sendNetworkStateChangeBroadcast(mLastBssid);
+
+ /* Reset data structures */
+ mWifiInfo.setIpAddress(0);
+ mWifiInfo.setBSSID(null);
+ mWifiInfo.setSSID(null);
+ mWifiInfo.setNetworkId(-1);
+
+ /* Clear network properties */
+ mNetworkProperties.clear();
+
+ mLastBssid= null;
+ mLastNetworkId = -1;
+
+ }
+
+
+ /*********************************************************
+ * Notifications from WifiMonitor
+ ********************************************************/
+
+ /**
+ * A structure for supplying information about a supplicant state
+ * change in the STATE_CHANGE event message that comes from the
+ * WifiMonitor
+ * thread.
+ */
+ private static class StateChangeResult {
+ StateChangeResult(int networkId, String BSSID, Object state) {
+ this.state = state;
+ this.BSSID = BSSID;
+ this.networkId = networkId;
+ }
+ int networkId;
+ String BSSID;
+ Object state;
+ }
+
+ /**
+ * Send the tracker a notification that a user-entered password key
+ * may be incorrect (i.e., caused authentication to fail).
+ */
+ void notifyPasswordKeyMayBeIncorrect() {
+ sendMessage(PASSWORD_MAY_BE_INCORRECT_EVENT);
+ }
+
+ /**
+ * Send the tracker a notification that a connection to the supplicant
+ * daemon has been established.
+ */
+ void notifySupplicantConnection() {
+ sendMessage(SUP_CONNECTION_EVENT);
+ }
+
+ /**
+ * Send the tracker a notification that a connection to the supplicant
+ * daemon has been established.
+ */
+ void notifySupplicantLost() {
+ sendMessage(SUP_DISCONNECTION_EVENT);
+ }
+
+ /**
+ * Send the tracker a notification that the state of Wifi connectivity
+ * has changed.
+ * @param networkId the configured network on which the state change occurred
+ * @param newState the new network state
+ * @param BSSID when the new state is {@link DetailedState#CONNECTED
+ * NetworkInfo.DetailedState.CONNECTED},
+ * this is the MAC address of the access point. Otherwise, it
+ * is {@code null}.
+ */
+ void notifyNetworkStateChange(DetailedState newState, String BSSID, int networkId) {
+ if (newState == NetworkInfo.DetailedState.CONNECTED) {
+ sendMessage(obtainMessage(NETWORK_CONNECTION_EVENT,
+ new StateChangeResult(networkId, BSSID, newState)));
+ } else {
+ sendMessage(obtainMessage(NETWORK_DISCONNECTION_EVENT,
+ new StateChangeResult(networkId, BSSID, newState)));
+ }
+ }
+
+ /**
+ * Send the tracker a notification that the state of the supplicant
+ * has changed.
+ * @param networkId the configured network on which the state change occurred
+ * @param newState the new {@code SupplicantState}
+ */
+ void notifySupplicantStateChange(int networkId, String BSSID, SupplicantState newState) {
+ sendMessage(obtainMessage(SUPPLICANT_STATE_CHANGE_EVENT,
+ new StateChangeResult(networkId, BSSID, newState)));
+ }
+
+ /**
+ * Send the tracker a notification that a scan has completed, and results
+ * are available.
+ */
+ void notifyScanResultsAvailable() {
+ /**
+ * Switch scan mode over to passive.
+ * Turning off scan-only mode happens only in "Connect" mode
+ */
+ setScanType(false);
+ sendMessage(SCAN_RESULTS_EVENT);
+ }
+
+ void notifyDriverStarted() {
+ sendMessage(DRIVER_START_EVENT);
+ }
+
+ void notifyDriverStopped() {
+ sendMessage(DRIVER_STOP_EVENT);
+ }
+
+ void notifyDriverHung() {
+ setWifiEnabled(false);
+ setWifiEnabled(true);
+ }
+
+
+ /********************************************************
+ * HSM states
+ *******************************************************/
+
+ class DefaultState extends HierarchicalState {
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ SyncParams syncParams;
+ switch (message.what) {
+ /* Synchronous call returns */
+ case CMD_PING_SUPPLICANT:
+ case CMD_START_SCAN:
+ case CMD_DISCONNECT:
+ case CMD_RECONNECT:
+ case CMD_REASSOCIATE:
+ case CMD_REMOVE_NETWORK:
+ case CMD_ENABLE_NETWORK:
+ case CMD_DISABLE_NETWORK:
+ case CMD_ADD_OR_UPDATE_NETWORK:
+ case CMD_GET_RSSI:
+ case CMD_GET_RSSI_APPROX:
+ case CMD_GET_LINK_SPEED:
+ case CMD_GET_MAC_ADDR:
+ case CMD_SAVE_CONFIG:
+ case CMD_CONNECTION_STATUS:
+ case CMD_GET_NETWORK_CONFIG:
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = false;
+ syncParams.mSyncReturn.intValue = -1;
+ syncParams.mSyncReturn.stringValue = null;
+ syncParams.mSyncReturn.configList = null;
+ notifyOnMsgObject(message);
+ }
+ break;
+ case CMD_ENABLE_RSSI_POLL:
+ mEnableRssiPolling = (message.arg1 == 1);
+ mSupplicantStateTracker.sendMessage(CMD_ENABLE_RSSI_POLL);
+ break;
+ /* Discard */
+ case CMD_LOAD_DRIVER:
+ case CMD_UNLOAD_DRIVER:
+ case CMD_START_SUPPLICANT:
+ case CMD_STOP_SUPPLICANT:
+ case CMD_START_DRIVER:
+ case CMD_STOP_DRIVER:
+ case CMD_START_AP:
+ case CMD_STOP_AP:
+ case CMD_RECONFIGURE_IP:
+ case SUP_CONNECTION_EVENT:
+ case SUP_DISCONNECTION_EVENT:
+ case DRIVER_START_EVENT:
+ case DRIVER_STOP_EVENT:
+ case NETWORK_CONNECTION_EVENT:
+ case NETWORK_DISCONNECTION_EVENT:
+ case SCAN_RESULTS_EVENT:
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ case PASSWORD_MAY_BE_INCORRECT_EVENT:
+ case CMD_BLACKLIST_NETWORK:
+ case CMD_CLEAR_BLACKLIST:
+ case CMD_SET_SCAN_MODE:
+ case CMD_SET_SCAN_TYPE:
+ case CMD_SET_POWER_MODE:
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ case CMD_REQUEST_CM_WAKELOCK:
+ break;
+ default:
+ Log.e(TAG, "Error! unhandled message" + message);
+ break;
+ }
+ return HANDLED;
+ }
+ }
+
+ class InitialState extends HierarchicalState {
+ @Override
+ //TODO: could move logging into a common class
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ // [31-8] Reserved for future use
+ // [7 - 0] HSM state change
+ // 50021 wifi_state_changed (custom|1|5)
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+ if (WifiNative.isDriverLoaded()) {
+ transitionTo(mDriverLoadedState);
+ }
+ else {
+ transitionTo(mDriverUnloadedState);
+ }
+ }
+ }
+
+ class DriverLoadingState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+ final Message message = new Message();
+ message.copyFrom(getCurrentMessage());
+ /* TODO: add a timeout to fail when driver load is hung.
+ * Similarly for driver unload.
+ */
+ new Thread(new Runnable() {
+ public void run() {
+ sWakeLock.acquire();
+ //enabling state
+ switch(message.arg1) {
+ case WIFI_STATE_ENABLING:
+ setWifiState(WIFI_STATE_ENABLING);
+ break;
+ case WIFI_AP_STATE_ENABLING:
+ setWifiApState(WIFI_AP_STATE_ENABLING);
+ break;
+ }
+
+ if(WifiNative.loadDriver()) {
+ Log.d(TAG, "Driver load successful");
+ sendMessage(CMD_LOAD_DRIVER_SUCCESS);
+ } else {
+ Log.e(TAG, "Failed to load driver!");
+ switch(message.arg1) {
+ case WIFI_STATE_ENABLING:
+ setWifiState(WIFI_STATE_UNKNOWN);
+ break;
+ case WIFI_AP_STATE_ENABLING:
+ setWifiApState(WIFI_AP_STATE_FAILED);
+ break;
+ }
+ sendMessage(CMD_LOAD_DRIVER_FAILURE);
+ }
+ sWakeLock.release();
+ }
+ }).start();
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_LOAD_DRIVER_SUCCESS:
+ transitionTo(mDriverLoadedState);
+ break;
+ case CMD_LOAD_DRIVER_FAILURE:
+ transitionTo(mDriverFailedState);
+ break;
+ case CMD_LOAD_DRIVER:
+ case CMD_UNLOAD_DRIVER:
+ case CMD_START_SUPPLICANT:
+ case CMD_STOP_SUPPLICANT:
+ case CMD_START_AP:
+ case CMD_STOP_AP:
+ case CMD_START_DRIVER:
+ case CMD_STOP_DRIVER:
+ case CMD_SET_SCAN_MODE:
+ case CMD_SET_SCAN_TYPE:
+ case CMD_SET_POWER_MODE:
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ case CMD_START_PACKET_FILTERING:
+ case CMD_STOP_PACKET_FILTERING:
+ deferMessage(message);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverLoadedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch(message.what) {
+ case CMD_UNLOAD_DRIVER:
+ transitionTo(mDriverUnloadingState);
+ break;
+ case CMD_START_SUPPLICANT:
+ if(WifiNative.startSupplicant()) {
+ Log.d(TAG, "Supplicant start successful");
+ mWifiMonitor.startMonitoring();
+ setWifiState(WIFI_STATE_ENABLED);
+ transitionTo(mWaitForSupState);
+ } else {
+ Log.e(TAG, "Failed to start supplicant!");
+ sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
+ }
+ break;
+ case CMD_START_AP:
+ try {
+ nwService.startAccessPoint((WifiConfiguration) message.obj,
+ mInterfaceName,
+ SOFTAP_IFACE);
+ } catch(Exception e) {
+ Log.e(TAG, "Exception in startAccessPoint()");
+ sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0));
+ break;
+ }
+ Log.d(TAG, "Soft AP start successful");
+ setWifiApState(WIFI_AP_STATE_ENABLED);
+ transitionTo(mSoftApStartedState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverUnloadingState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+ final Message message = new Message();
+ message.copyFrom(getCurrentMessage());
+ new Thread(new Runnable() {
+ public void run() {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ sWakeLock.acquire();
+ if(WifiNative.unloadDriver()) {
+ Log.d(TAG, "Driver unload successful");
+ sendMessage(CMD_UNLOAD_DRIVER_SUCCESS);
+
+ switch(message.arg1) {
+ case WIFI_STATE_DISABLED:
+ case WIFI_STATE_UNKNOWN:
+ setWifiState(message.arg1);
+ break;
+ case WIFI_AP_STATE_DISABLED:
+ case WIFI_AP_STATE_FAILED:
+ setWifiApState(message.arg1);
+ break;
+ }
+ } else {
+ Log.e(TAG, "Failed to unload driver!");
+ sendMessage(CMD_UNLOAD_DRIVER_FAILURE);
+
+ switch(message.arg1) {
+ case WIFI_STATE_DISABLED:
+ case WIFI_STATE_UNKNOWN:
+ setWifiState(WIFI_STATE_UNKNOWN);
+ break;
+ case WIFI_AP_STATE_DISABLED:
+ case WIFI_AP_STATE_FAILED:
+ setWifiApState(WIFI_AP_STATE_FAILED);
+ break;
+ }
+ }
+ sWakeLock.release();
+ }
+ }).start();
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_UNLOAD_DRIVER_SUCCESS:
+ transitionTo(mDriverUnloadedState);
+ break;
+ case CMD_UNLOAD_DRIVER_FAILURE:
+ transitionTo(mDriverFailedState);
+ break;
+ case CMD_LOAD_DRIVER:
+ case CMD_UNLOAD_DRIVER:
+ case CMD_START_SUPPLICANT:
+ case CMD_STOP_SUPPLICANT:
+ case CMD_START_AP:
+ case CMD_STOP_AP:
+ case CMD_START_DRIVER:
+ case CMD_STOP_DRIVER:
+ case CMD_SET_SCAN_MODE:
+ case CMD_SET_SCAN_TYPE:
+ case CMD_SET_POWER_MODE:
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ case CMD_START_PACKET_FILTERING:
+ case CMD_STOP_PACKET_FILTERING:
+ deferMessage(message);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverUnloadedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_LOAD_DRIVER:
+ transitionTo(mDriverLoadingState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverFailedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ Log.e(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ return NOT_HANDLED;
+ }
+ }
+
+
+ class WaitForSupState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch(message.what) {
+ case SUP_CONNECTION_EVENT:
+ Log.d(TAG, "Supplicant connection established");
+ mSupplicantStateTracker.resetSupplicantState();
+ /* Initialize data structures */
+ mLastBssid = null;
+ mLastNetworkId = -1;
+ mLastSignalLevel = -1;
+
+ mWifiInfo.setMacAddress(WifiNative.getMacAddressCommand());
+
+ //TODO: initialize and fix multicast filtering
+ //mWM.initializeMulticastFiltering();
+
+ if (mBluetoothA2dp == null) {
+ mBluetoothA2dp = new BluetoothA2dp(mContext);
+ }
+ checkIsBluetoothPlaying();
+
+ checkUseStaticIp();
+ sendSupplicantConnectionChangedBroadcast(true);
+ transitionTo(mDriverSupReadyState);
+ break;
+ case CMD_STOP_SUPPLICANT:
+ Log.d(TAG, "Stop supplicant received");
+ WifiNative.stopSupplicant();
+ transitionTo(mDriverLoadedState);
+ break;
+ /* Fail soft ap when waiting for supplicant start */
+ case CMD_START_AP:
+ Log.d(TAG, "Failed to start soft AP with a running supplicant");
+ setWifiApState(WIFI_AP_STATE_FAILED);
+ break;
+ case CMD_START_DRIVER:
+ case CMD_STOP_DRIVER:
+ case CMD_SET_SCAN_MODE:
+ case CMD_SET_SCAN_TYPE:
+ case CMD_SET_POWER_MODE:
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ case CMD_START_PACKET_FILTERING:
+ case CMD_STOP_PACKET_FILTERING:
+ deferMessage(message);
+ break;
+ case CMD_STOP_AP:
+ case CMD_START_SUPPLICANT:
+ case CMD_UNLOAD_DRIVER:
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverSupReadyState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ /* Initialize for connect mode operation at start */
+ mIsScanMode = false;
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ SyncParams syncParams;
+ switch(message.what) {
+ case CMD_STOP_SUPPLICANT: /* Supplicant stopped by user */
+ Log.d(TAG, "Stop supplicant received");
+ WifiNative.stopSupplicant();
+ //$FALL-THROUGH$
+ case SUP_DISCONNECTION_EVENT: /* Supplicant died */
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ WifiNative.closeSupplicantConnection();
+ handleNetworkDisconnect();
+ sendSupplicantConnectionChangedBroadcast(false);
+ mSupplicantStateTracker.resetSupplicantState();
+ transitionTo(mDriverLoadedState);
+
+ /* When supplicant dies, unload driver and enter failed state */
+ //TODO: consider bringing up supplicant again
+ if (message.what == SUP_DISCONNECTION_EVENT) {
+ Log.d(TAG, "Supplicant died, unloading driver");
+ sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
+ }
+ break;
+ case CMD_START_DRIVER:
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ WifiNative.startDriverCommand();
+ transitionTo(mDriverStartingState);
+ break;
+ case SCAN_RESULTS_EVENT:
+ setScanResults(WifiNative.scanResultsCommand());
+ sendScanResultsAvailableBroadcast();
+ break;
+ case CMD_PING_SUPPLICANT:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.pingCommand();
+ notifyOnMsgObject(message);
+ break;
+ case CMD_ADD_OR_UPDATE_NETWORK:
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ syncParams = (SyncParams) message.obj;
+ WifiConfiguration config = (WifiConfiguration) syncParams.mParameter;
+ syncParams.mSyncReturn.intValue = addOrUpdateNetworkNative(config);
+ notifyOnMsgObject(message);
+ break;
+ case CMD_REMOVE_NETWORK:
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.removeNetworkCommand(
+ message.arg1);
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.removeNetworkCommand(message.arg1);
+ }
+ break;
+ case CMD_ENABLE_NETWORK:
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ EnableNetParams enableNetParams = (EnableNetParams) syncParams.mParameter;
+ syncParams.mSyncReturn.boolValue = WifiNative.enableNetworkCommand(
+ enableNetParams.netId, enableNetParams.disableOthers);
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.enableNetworkCommand(message.arg1, message.arg2 == 1);
+ }
+ break;
+ case CMD_DISABLE_NETWORK:
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.disableNetworkCommand(
+ message.arg1);
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.disableNetworkCommand(message.arg1);
+ }
+ break;
+ case CMD_BLACKLIST_NETWORK:
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ WifiNative.addToBlacklistCommand((String)message.obj);
+ break;
+ case CMD_CLEAR_BLACKLIST:
+ WifiNative.clearBlacklistCommand();
+ break;
+ case CMD_GET_NETWORK_CONFIG:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.configList = getConfiguredNetworksNative();
+ notifyOnMsgObject(message);
+ break;
+ case CMD_SAVE_CONFIG:
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.saveConfigCommand();
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.saveConfigCommand();
+ }
+ // Inform the backup manager about a data change
+ IBackupManager ibm = IBackupManager.Stub.asInterface(
+ ServiceManager.getService(Context.BACKUP_SERVICE));
+ if (ibm != null) {
+ try {
+ ibm.dataChanged("com.android.providers.settings");
+ } catch (Exception e) {
+ // Try again later
+ }
+ }
+ break;
+ case CMD_CONNECTION_STATUS:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.stringValue = WifiNative.statusCommand();
+ notifyOnMsgObject(message);
+ break;
+ case CMD_GET_MAC_ADDR:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.stringValue = WifiNative.getMacAddressCommand();
+ notifyOnMsgObject(message);
+ break;
+ /* Cannot start soft AP while in client mode */
+ case CMD_START_AP:
+ Log.d(TAG, "Failed to start soft AP with a running supplicant");
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ setWifiApState(WIFI_AP_STATE_FAILED);
+ break;
+ case CMD_SET_SCAN_MODE:
+ mIsScanMode = (message.arg1 == SCAN_ONLY_MODE);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class DriverStartingState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch(message.what) {
+ case DRIVER_START_EVENT:
+ transitionTo(mDriverStartedState);
+ break;
+ /* Queue driver commands & connection events */
+ case CMD_START_DRIVER:
+ case CMD_STOP_DRIVER:
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ case NETWORK_CONNECTION_EVENT:
+ case NETWORK_DISCONNECTION_EVENT:
+ case PASSWORD_MAY_BE_INCORRECT_EVENT:
+ case CMD_SET_SCAN_TYPE:
+ case CMD_SET_POWER_MODE:
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ case CMD_START_PACKET_FILTERING:
+ case CMD_STOP_PACKET_FILTERING:
+ deferMessage(message);
+ break;
+ /* Queue the asynchronous version of these commands */
+ case CMD_START_SCAN:
+ case CMD_DISCONNECT:
+ case CMD_REASSOCIATE:
+ case CMD_RECONNECT:
+ if (message.arg2 != SYNCHRONOUS_CALL) {
+ deferMessage(message);
+ }
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverStartedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+ try {
+ mBatteryStats.noteWifiRunning();
+ } catch (RemoteException ignore) {}
+
+ /* Initialize channel count */
+ setNumAllowedChannels();
+
+ if (mIsScanMode) {
+ WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
+ WifiNative.disconnectCommand();
+ transitionTo(mScanModeState);
+ } else {
+ WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
+ /* If supplicant has already connected, before we could finish establishing
+ * the control channel connection, we miss all the supplicant events.
+ * Disconnect and reconnect when driver has started to ensure we receive
+ * all supplicant events.
+ *
+ * TODO: This is a bit unclean, ideally the supplicant should never
+ * connect until told to do so by the framework
+ */
+ WifiNative.disconnectCommand();
+ WifiNative.reconnectCommand();
+ transitionTo(mConnectModeState);
+ }
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ SyncParams syncParams;
+ switch(message.what) {
+ case CMD_SET_SCAN_TYPE:
+ if (message.arg1 == SCAN_ACTIVE) {
+ WifiNative.setScanModeCommand(true);
+ } else {
+ WifiNative.setScanModeCommand(false);
+ }
+ break;
+ case CMD_SET_POWER_MODE:
+ WifiNative.setPowerModeCommand(message.arg1);
+ break;
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ WifiNative.setBluetoothCoexistenceModeCommand(message.arg1);
+ break;
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ WifiNative.setBluetoothCoexistenceScanModeCommand(message.arg1 == 1);
+ break;
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ mNumAllowedChannels = message.arg1;
+ WifiNative.setNumAllowedChannelsCommand(message.arg1);
+ break;
+ case CMD_START_DRIVER:
+ /* Ignore another driver start */
+ break;
+ case CMD_STOP_DRIVER:
+ WifiNative.stopDriverCommand();
+ transitionTo(mDriverStoppingState);
+ break;
+ case CMD_REQUEST_CM_WAKELOCK:
+ if (mCm == null) {
+ mCm = (ConnectivityManager)mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ }
+ mCm.requestNetworkTransitionWakelock(TAG);
+ break;
+ case CMD_START_PACKET_FILTERING:
+ WifiNative.startPacketFiltering();
+ break;
+ case CMD_STOP_PACKET_FILTERING:
+ WifiNative.stopPacketFiltering();
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ @Override
+ public void exit() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ try {
+ mBatteryStats.noteWifiStopped();
+ } catch (RemoteException ignore) { }
+ }
+ }
+
+ class DriverStoppingState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch(message.what) {
+ case DRIVER_STOP_EVENT:
+ transitionTo(mDriverStoppedState);
+ break;
+ /* Queue driver commands */
+ case CMD_START_DRIVER:
+ case CMD_STOP_DRIVER:
+ case CMD_SET_SCAN_TYPE:
+ case CMD_SET_POWER_MODE:
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ case CMD_START_PACKET_FILTERING:
+ case CMD_STOP_PACKET_FILTERING:
+ deferMessage(message);
+ break;
+ /* Queue the asynchronous version of these commands */
+ case CMD_START_SCAN:
+ case CMD_DISCONNECT:
+ case CMD_REASSOCIATE:
+ case CMD_RECONNECT:
+ if (message.arg2 != SYNCHRONOUS_CALL) {
+ deferMessage(message);
+ }
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverStoppedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ return NOT_HANDLED;
+ }
+ }
+
+ class ScanModeState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ SyncParams syncParams;
+ switch(message.what) {
+ case CMD_SET_SCAN_MODE:
+ if (message.arg1 == SCAN_ONLY_MODE) {
+ /* Ignore */
+ return HANDLED;
+ } else {
+ WifiNative.setScanResultHandlingCommand(message.arg1);
+ WifiNative.reconnectCommand();
+ mIsScanMode = false;
+ transitionTo(mDisconnectedState);
+ }
+ break;
+ case CMD_START_SCAN:
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.scanCommand(
+ message.arg1 == SCAN_ACTIVE);
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
+ }
+ break;
+ /* Ignore */
+ case CMD_DISCONNECT:
+ case CMD_RECONNECT:
+ case CMD_REASSOCIATE:
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ case NETWORK_CONNECTION_EVENT:
+ case NETWORK_DISCONNECTION_EVENT:
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class ConnectModeState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ SyncParams syncParams;
+ StateChangeResult stateChangeResult;
+ switch(message.what) {
+ case PASSWORD_MAY_BE_INCORRECT_EVENT:
+ mPasswordKeyMayBeIncorrect = true;
+ break;
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ stateChangeResult = (StateChangeResult) message.obj;
+ mSupplicantStateTracker.handleEvent(stateChangeResult);
+ break;
+ case CMD_START_SCAN:
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = true;
+ notifyOnMsgObject(message);
+ }
+ /* We need to set scan type in completed state */
+ Message newMsg = obtainMessage();
+ newMsg.copyFrom(message);
+ mSupplicantStateTracker.sendMessage(newMsg);
+ break;
+ /* Do a redundant disconnect without transition */
+ case CMD_DISCONNECT:
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand();
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.disconnectCommand();
+ }
+ break;
+ case CMD_RECONNECT:
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.reconnectCommand();
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.reconnectCommand();
+ }
+ break;
+ case CMD_REASSOCIATE:
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.reassociateCommand();
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.reassociateCommand();
+ }
+ break;
+ case SCAN_RESULTS_EVENT:
+ /* Set the scan setting back to "connect" mode */
+ WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
+ /* Handle scan results */
+ return NOT_HANDLED;
+ case NETWORK_CONNECTION_EVENT:
+ Log.d(TAG,"Network connection established");
+ stateChangeResult = (StateChangeResult) message.obj;
+
+ mWifiInfo.setBSSID(mLastBssid = stateChangeResult.BSSID);
+ mWifiInfo.setNetworkId(stateChangeResult.networkId);
+ mLastNetworkId = stateChangeResult.networkId;
+
+ /* send event to CM & network change broadcast */
+ setDetailedState(DetailedState.OBTAINING_IPADDR);
+ sendNetworkStateChangeBroadcast(mLastBssid);
+
+ transitionTo(mConnectingState);
+ break;
+ case NETWORK_DISCONNECTION_EVENT:
+ Log.d(TAG,"Network connection lost");
+ handleNetworkDisconnect();
+ transitionTo(mDisconnectedState);
+ break;
+ case CMD_GET_RSSI:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.intValue = WifiNative.getRssiCommand();
+ notifyOnMsgObject(message);
+ break;
+ case CMD_GET_RSSI_APPROX:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.intValue = WifiNative.getRssiApproxCommand();
+ notifyOnMsgObject(message);
+ break;
+ case CMD_GET_LINK_SPEED:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.intValue = WifiNative.getLinkSpeedCommand();
+ notifyOnMsgObject(message);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class ConnectingState extends HierarchicalState {
+ boolean modifiedBluetoothCoexistenceMode;
+ int powerMode;
+ Thread mDhcpThread;
+
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+ if (!mUseStaticIp) {
+
+ mDhcpThread = null;
+ modifiedBluetoothCoexistenceMode = false;
+ powerMode = DRIVER_POWER_MODE_AUTO;
+
+ if (shouldDisableCoexistenceMode()) {
+ /*
+ * There are problems setting the Wi-Fi driver's power
+ * mode to active when bluetooth coexistence mode is
+ * enabled or sense.
+ * <p>
+ * We set Wi-Fi to active mode when
+ * obtaining an IP address because we've found
+ * compatibility issues with some routers with low power
+ * mode.
+ * <p>
+ * In order for this active power mode to properly be set,
+ * we disable coexistence mode until we're done with
+ * obtaining an IP address. One exception is if we
+ * are currently connected to a headset, since disabling
+ * coexistence would interrupt that connection.
+ */
+ modifiedBluetoothCoexistenceMode = true;
+
+ // Disable the coexistence mode
+ WifiNative.setBluetoothCoexistenceModeCommand(
+ WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
+ }
+
+ powerMode = WifiNative.getPowerModeCommand();
+ if (powerMode < 0) {
+ // Handle the case where supplicant driver does not support
+ // getPowerModeCommand.
+ powerMode = DRIVER_POWER_MODE_AUTO;
+ }
+ if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
+ WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_ACTIVE);
+ }
+
+ Log.d(TAG, "DHCP request started");
+ mDhcpThread = new Thread(new Runnable() {
+ public void run() {
+ if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {
+ Log.d(TAG, "DHCP request succeeded");
+ sendMessage(CMD_IP_CONFIG_SUCCESS);
+ } else {
+ Log.d(TAG, "DHCP request failed: " +
+ NetworkUtils.getDhcpError());
+ sendMessage(CMD_IP_CONFIG_FAILURE);
+ }
+ }
+ });
+ mDhcpThread.start();
+ } else {
+ if (NetworkUtils.configureInterface(mInterfaceName, mDhcpInfo)) {
+ Log.v(TAG, "Static IP configuration succeeded");
+ sendMessage(CMD_IP_CONFIG_SUCCESS);
+ } else {
+ Log.v(TAG, "Static IP configuration failed");
+ sendMessage(CMD_IP_CONFIG_FAILURE);
+ }
+ }
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+
+ switch(message.what) {
+ case CMD_IP_CONFIG_SUCCESS:
+ mReconnectCount = 0;
+ mLastSignalLevel = -1; // force update of signal strength
+ mWifiInfo.setIpAddress(mDhcpInfo.ipAddress);
+ Log.d(TAG, "IP configuration: " + mDhcpInfo);
+ configureNetworkProperties();
+ setDetailedState(DetailedState.CONNECTED);
+ sendNetworkStateChangeBroadcast(mLastBssid);
+ //TODO: we could also detect an IP config change
+ // from a DHCP renewal and send out a config change
+ // broadcast
+ if (mConfigChanged) {
+ sendConfigChangeBroadcast();
+ mConfigChanged = false;
+ }
+ transitionTo(mConnectedState);
+ break;
+ case CMD_IP_CONFIG_FAILURE:
+ mWifiInfo.setIpAddress(0);
+
+ Log.e(TAG, "IP configuration failed");
+ /**
+ * If we've exceeded the maximum number of retries for DHCP
+ * to a given network, disable the network
+ */
+ if (++mReconnectCount > getMaxDhcpRetries()) {
+ Log.e(TAG, "Failed " +
+ mReconnectCount + " times, Disabling " + mLastNetworkId);
+ WifiNative.disableNetworkCommand(mLastNetworkId);
+ }
+
+ /* DHCP times out after about 30 seconds, we do a
+ * disconnect and an immediate reconnect to try again
+ */
+ WifiNative.disconnectCommand();
+ WifiNative.reconnectCommand();
+ transitionTo(mDisconnectingState);
+ break;
+ case CMD_DISCONNECT:
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ SyncParams syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand();
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.disconnectCommand();
+ }
+ transitionTo(mDisconnectingState);
+ break;
+ /* Ignore */
+ case NETWORK_CONNECTION_EVENT:
+ break;
+ case CMD_STOP_DRIVER:
+ sendMessage(CMD_DISCONNECT);
+ deferMessage(message);
+ break;
+ case CMD_SET_SCAN_MODE:
+ if (message.arg1 == SCAN_ONLY_MODE) {
+ sendMessage(CMD_DISCONNECT);
+ deferMessage(message);
+ }
+ break;
+ case CMD_RECONFIGURE_IP:
+ deferMessage(message);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+
+ @Override
+ public void exit() {
+ /* reset power state & bluetooth coexistence if on DHCP */
+ if (!mUseStaticIp) {
+ if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
+ WifiNative.setPowerModeCommand(powerMode);
+ }
+
+ if (modifiedBluetoothCoexistenceMode) {
+ // Set the coexistence mode back to its default value
+ WifiNative.setBluetoothCoexistenceModeCommand(
+ WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
+ }
+ }
+
+ }
+ }
+
+ class ConnectedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_DISCONNECT:
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ SyncParams syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand();
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.disconnectCommand();
+ }
+ transitionTo(mDisconnectingState);
+ break;
+ case CMD_RECONFIGURE_IP:
+ Log.d(TAG,"Reconfiguring IP on connection");
+ NetworkUtils.resetConnections(mInterfaceName);
+ transitionTo(mConnectingState);
+ break;
+ case CMD_STOP_DRIVER:
+ sendMessage(CMD_DISCONNECT);
+ deferMessage(message);
+ break;
+ case CMD_SET_SCAN_MODE:
+ if (message.arg1 == SCAN_ONLY_MODE) {
+ sendMessage(CMD_DISCONNECT);
+ deferMessage(message);
+ }
+ break;
+ /* Ignore */
+ case NETWORK_CONNECTION_EVENT:
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DisconnectingState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_STOP_DRIVER: /* Stop driver only after disconnect handled */
+ deferMessage(message);
+ break;
+ case CMD_SET_SCAN_MODE:
+ if (message.arg1 == SCAN_ONLY_MODE) {
+ deferMessage(message);
+ }
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DisconnectedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_SET_SCAN_MODE:
+ if (message.arg1 == SCAN_ONLY_MODE) {
+ WifiNative.setScanResultHandlingCommand(message.arg1);
+ //Supplicant disconnect to prevent further connects
+ WifiNative.disconnectCommand();
+ mIsScanMode = true;
+ transitionTo(mScanModeState);
+ }
+ break;
+ /* Ignore network disconnect */
+ case NETWORK_DISCONNECTION_EVENT:
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class SoftApStartedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch(message.what) {
+ case CMD_STOP_AP:
+ Log.d(TAG,"Stopping Soft AP");
+ setWifiApState(WIFI_AP_STATE_DISABLING);
+ try {
+ nwService.stopAccessPoint();
+ } catch(Exception e) {
+ Log.e(TAG, "Exception in stopAccessPoint()");
+ }
+ transitionTo(mDriverLoadedState);
+ break;
+ case CMD_START_AP:
+ Log.d(TAG,"SoftAP set on a running access point");
+ try {
+ nwService.setAccessPoint((WifiConfiguration) message.obj,
+ mInterfaceName,
+ SOFTAP_IFACE);
+ } catch(Exception e) {
+ Log.e(TAG, "Exception in nwService during soft AP set");
+ try {
+ nwService.stopAccessPoint();
+ } catch (Exception ee) {
+ Slog.e(TAG, "Could not stop AP, :" + ee);
+ }
+ sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0));
+ }
+ break;
+ /* Fail client mode operation when soft AP is enabled */
+ case CMD_START_SUPPLICANT:
+ Log.e(TAG,"Cannot start supplicant with a running soft AP");
+ setWifiState(WIFI_STATE_UNKNOWN);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+
+ class SupplicantStateTracker extends HierarchicalStateMachine {
+
+ private int mRssiPollToken = 0;
+
+ /**
+ * The max number of the WPA supplicant loop iterations before we
+ * decide that the loop should be terminated:
+ */
+ private static final int MAX_SUPPLICANT_LOOP_ITERATIONS = 4;
+ private int mLoopDetectIndex = 0;
+ private int mLoopDetectCount = 0;
+
+ /**
+ * Supplicant state change commands follow
+ * the ordinal values defined in SupplicantState.java
+ */
+ private static final int DISCONNECTED = 0;
+ private static final int INACTIVE = 1;
+ private static final int SCANNING = 2;
+ private static final int ASSOCIATING = 3;
+ private static final int ASSOCIATED = 4;
+ private static final int FOUR_WAY_HANDSHAKE = 5;
+ private static final int GROUP_HANDSHAKE = 6;
+ private static final int COMPLETED = 7;
+ private static final int DORMANT = 8;
+ private static final int UNINITIALIZED = 9;
+ private static final int INVALID = 10;
+
+ private HierarchicalState mUninitializedState = new UninitializedState();
+ private HierarchicalState mInitializedState = new InitializedState();;
+ private HierarchicalState mInactiveState = new InactiveState();
+ private HierarchicalState mDisconnectState = new DisconnectedState();
+ private HierarchicalState mScanState = new ScanState();
+ private HierarchicalState mConnectState = new ConnectState();
+ private HierarchicalState mHandshakeState = new HandshakeState();
+ private HierarchicalState mCompletedState = new CompletedState();
+ private HierarchicalState mDormantState = new DormantState();
+
+ public SupplicantStateTracker(Context context, Handler target) {
+ super(TAG, target.getLooper());
+
+ addState(mUninitializedState);
+ addState(mInitializedState);
+ addState(mInactiveState, mInitializedState);
+ addState(mDisconnectState, mInitializedState);
+ addState(mScanState, mInitializedState);
+ addState(mConnectState, mInitializedState);
+ addState(mHandshakeState, mConnectState);
+ addState(mCompletedState, mConnectState);
+ addState(mDormantState, mInitializedState);
+
+ setInitialState(mUninitializedState);
+
+ //start the state machine
+ start();
+ }
+
+ public void handleEvent(StateChangeResult stateChangeResult) {
+ SupplicantState newState = (SupplicantState) stateChangeResult.state;
+
+ // Supplicant state change
+ // [31-13] Reserved for future use
+ // [8 - 0] Supplicant state (as defined in SupplicantState.java)
+ // 50023 supplicant_state_changed (custom|1|5)
+ EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, newState.ordinal());
+
+ sendMessage(obtainMessage(newState.ordinal(), stateChangeResult));
+ }
+
+ public void resetSupplicantState() {
+ transitionTo(mUninitializedState);
+ }
+
+ private void resetLoopDetection() {
+ mLoopDetectCount = 0;
+ mLoopDetectIndex = 0;
+ }
+
+ private boolean handleTransition(Message msg) {
+ if (DBG) Log.d(TAG, getName() + msg.toString() + "\n");
+ switch (msg.what) {
+ case DISCONNECTED:
+ transitionTo(mDisconnectState);
+ break;
+ case SCANNING:
+ transitionTo(mScanState);
+ break;
+ case ASSOCIATING:
+ StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
+ /* BSSID is valid only in ASSOCIATING state */
+ mWifiInfo.setBSSID(stateChangeResult.BSSID);
+ //$FALL-THROUGH$
+ case ASSOCIATED:
+ case FOUR_WAY_HANDSHAKE:
+ case GROUP_HANDSHAKE:
+ transitionTo(mHandshakeState);
+ break;
+ case COMPLETED:
+ transitionTo(mCompletedState);
+ break;
+ case DORMANT:
+ transitionTo(mDormantState);
+ break;
+ case INACTIVE:
+ transitionTo(mInactiveState);
+ break;
+ case UNINITIALIZED:
+ case INVALID:
+ transitionTo(mUninitializedState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
+ SupplicantState supState = (SupplicantState) stateChangeResult.state;
+ setDetailedState(WifiInfo.getDetailedStateOf(supState));
+ mWifiInfo.setSupplicantState(supState);
+ mWifiInfo.setNetworkId(stateChangeResult.networkId);
+ //TODO: Modify WifiMonitor to report SSID on events
+ //mWifiInfo.setSSID()
+ return HANDLED;
+ }
+
+ /********************************************************
+ * HSM states
+ *******************************************************/
+
+ class InitializedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_START_SCAN:
+ WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
+ break;
+ default:
+ if (DBG) Log.w(TAG, "Ignoring " + message);
+ break;
+ }
+ return HANDLED;
+ }
+ }
+
+ class UninitializedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ mNetworkInfo.setIsAvailable(false);
+ resetLoopDetection();
+ mPasswordKeyMayBeIncorrect = false;
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ switch(message.what) {
+ default:
+ if (!handleTransition(message)) {
+ if (DBG) Log.w(TAG, "Ignoring " + message);
+ }
+ break;
+ }
+ return HANDLED;
+ }
+ @Override
+ public void exit() {
+ mNetworkInfo.setIsAvailable(true);
+ }
+ }
+
+ class InactiveState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ Message message = getCurrentMessage();
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+ mNetworkInfo.setIsAvailable(false);
+ resetLoopDetection();
+ mPasswordKeyMayBeIncorrect = false;
+
+ sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ return handleTransition(message);
+ }
+ @Override
+ public void exit() {
+ mNetworkInfo.setIsAvailable(true);
+ }
+ }
+
+
+ class DisconnectedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ Message message = getCurrentMessage();
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+ resetLoopDetection();
+
+ /* If a disconnect event happens after a password key failure
+ * event, disable the network
+ */
+ if (mPasswordKeyMayBeIncorrect) {
+ Log.d(TAG, "Failed to authenticate, disabling network " +
+ mWifiInfo.getNetworkId());
+ WifiNative.disableNetworkCommand(mWifiInfo.getNetworkId());
+ mPasswordKeyMayBeIncorrect = false;
+ sendSupplicantStateChangedBroadcast(stateChangeResult, true);
+ }
+ else {
+ sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+ }
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ return handleTransition(message);
+ }
+ }
+
+ class ScanState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ Message message = getCurrentMessage();
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+ mPasswordKeyMayBeIncorrect = false;
+ resetLoopDetection();
+ sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ return handleTransition(message);
+ }
+ }
+
+ class ConnectState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ switch (message.what) {
+ case CMD_START_SCAN:
+ WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
+ WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class HandshakeState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ final Message message = getCurrentMessage();
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+ if (mLoopDetectIndex > message.what) {
+ mLoopDetectCount++;
+ }
+ if (mLoopDetectCount > MAX_SUPPLICANT_LOOP_ITERATIONS) {
+ WifiNative.disableNetworkCommand(stateChangeResult.networkId);
+ mLoopDetectCount = 0;
+ }
+
+ mLoopDetectIndex = message.what;
+
+ mPasswordKeyMayBeIncorrect = false;
+ sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ return handleTransition(message);
+ }
+ }
+
+ class CompletedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ Message message = getCurrentMessage();
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+ mRssiPollToken++;
+ if (mEnableRssiPolling) {
+ sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
+ POLL_RSSI_INTERVAL_MSECS);
+ }
+
+ resetLoopDetection();
+
+ mPasswordKeyMayBeIncorrect = false;
+ sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ switch(message.what) {
+ case ASSOCIATING:
+ case ASSOCIATED:
+ case FOUR_WAY_HANDSHAKE:
+ case GROUP_HANDSHAKE:
+ case COMPLETED:
+ break;
+ case CMD_RSSI_POLL:
+ if (message.arg1 == mRssiPollToken) {
+ // Get Info and continue polling
+ requestPolledInfo();
+ sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
+ POLL_RSSI_INTERVAL_MSECS);
+ } else {
+ // Polling has completed
+ }
+ break;
+ case CMD_ENABLE_RSSI_POLL:
+ mRssiPollToken++;
+ if (mEnableRssiPolling) {
+ // first poll
+ requestPolledInfo();
+ sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
+ POLL_RSSI_INTERVAL_MSECS);
+ }
+ break;
+ default:
+ return handleTransition(message);
+ }
+ return HANDLED;
+ }
+ }
+
+ class DormantState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ Message message = getCurrentMessage();
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+ resetLoopDetection();
+ mPasswordKeyMayBeIncorrect = false;
+
+ sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+
+ /* TODO: reconnect is now being handled at DHCP failure handling
+ * If we run into issues with staying in Dormant state, might
+ * need a reconnect here
+ */
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ return handleTransition(message);
+ }
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 388beea..8e1b236 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -16,524 +16,59 @@
package android.net.wifi;
-import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
-import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
-import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
-import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
-import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
-/**
- * TODO: Add soft AP states as part of WIFI_STATE_XXX
- * Retain WIFI_STATE_ENABLING that indicates driver is loading
- * Add WIFI_STATE_AP_ENABLED to indicate soft AP has started
- * and WIFI_STATE_FAILED for failure
- * Deprecate WIFI_STATE_UNKNOWN
- *
- * Doing this will simplify the logic for sending broadcasts
- */
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
-import android.app.ActivityManagerNative;
-import android.net.NetworkInfo;
-import android.net.NetworkStateTracker;
-import android.net.DhcpInfo;
-import android.net.NetworkUtils;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkInfo.State;
-import android.net.NetworkProperties;
-import android.os.Binder;
-import android.os.Message;
-import android.os.Parcelable;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.INetworkManagementService;
-import android.os.PowerManager;
-import android.os.SystemProperties;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.Process;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.EventLog;
-import android.util.Log;
-import android.util.Slog;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.backup.IBackupManager;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHeadset;
-import android.bluetooth.BluetoothA2dp;
-import android.content.ContentResolver;
-import android.content.Intent;
+import android.content.BroadcastReceiver;
import android.content.Context;
-import android.database.ContentObserver;
-import com.android.internal.app.IBatteryStats;
-import com.android.internal.util.HierarchicalState;
-import com.android.internal.util.HierarchicalStateMachine;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.NetworkProperties;
+import android.net.NetworkStateTracker;
+import android.os.Handler;
+import android.os.Message;
-import java.net.InetAddress;
-import java.net.NetworkInterface;
-import java.net.SocketException;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.BitSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.regex.Pattern;
/**
- * Track the state of Wifi connectivity. All event handling is done here,
- * and all changes in connectivity state are initiated here.
+ * Track the state of wifi for connectivity service.
*
* @hide
*/
-//TODO: we still need frequent scanning for the case when
-// we issue disconnect but need scan results for open network notification
-public class WifiStateTracker extends HierarchicalStateMachine implements NetworkStateTracker {
+public class WifiStateTracker implements NetworkStateTracker {
- private static final String TAG = "WifiStateTracker";
private static final String NETWORKTYPE = "WIFI";
- private static final boolean DBG = false;
+ private static final String TAG = "WifiStateTracker";
- /* TODO: fetch a configurable interface */
- private static final String SOFTAP_IFACE = "wl0.1";
-
- private WifiMonitor mWifiMonitor;
- private INetworkManagementService nwService;
- private ConnectivityManager mCm;
-
- /* Scan results handling */
- private List<ScanResult> mScanResults;
- private static final Pattern scanResultPattern = Pattern.compile("\t+");
- private static final int SCAN_RESULT_CACHE_SIZE = 80;
- private final LinkedHashMap<String, ScanResult> mScanResultCache;
-
- private String mInterfaceName;
private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0);
private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false);
- private int mNumAllowedChannels = 0;
- private int mLastSignalLevel = -1;
- private String mLastBssid;
- private int mLastNetworkId;
- private boolean mEnableRssiPolling = false;
- private boolean mPasswordKeyMayBeIncorrect = false;
- private boolean mUseStaticIp = false;
- private int mReconnectCount = 0;
- private boolean mIsScanMode = false;
-
- /**
- * Instance of the bluetooth headset helper. This needs to be created
- * early because there is a delay before it actually 'connects', as
- * noted by its javadoc. If we check before it is connected, it will be
- * in an error state and we will not disable coexistence.
- */
- private BluetoothHeadset mBluetoothHeadset;
-
- private BluetoothA2dp mBluetoothA2dp;
-
- /**
- * Observes the static IP address settings.
- */
- private SettingsObserver mSettingsObserver;
private NetworkProperties mNetworkProperties;
-
-
- // Variables relating to the 'available networks' notification
- /**
- * The icon to show in the 'available networks' notification. This will also
- * be the ID of the Notification given to the NotificationManager.
- */
- private static final int ICON_NETWORKS_AVAILABLE =
- com.android.internal.R.drawable.stat_notify_wifi_in_range;
- /**
- * When a notification is shown, we wait this amount before possibly showing it again.
- */
- private final long NOTIFICATION_REPEAT_DELAY_MS;
- /**
- * Whether the user has set the setting to show the 'available networks' notification.
- */
- private boolean mNotificationEnabled;
- /**
- * Observes the user setting to keep {@link #mNotificationEnabled} in sync.
- */
- private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver;
- /**
- * The {@link System#currentTimeMillis()} must be at least this value for us
- * to show the notification again.
- */
- private long mNotificationRepeatTime;
- /**
- * The Notification object given to the NotificationManager.
- */
- private Notification mNotification;
- /**
- * Whether the notification is being shown, as set by us. That is, if the
- * user cancels the notification, we will not receive the callback so this
- * will still be true. We only guarantee if this is false, then the
- * notification is not showing.
- */
- private boolean mNotificationShown;
- /**
- * The number of continuous scans that must occur before consider the
- * supplicant in a scanning state. This allows supplicant to associate with
- * remembered networks that are in the scan results.
- */
- private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3;
- /**
- * The number of scans since the last network state change. When this
- * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the
- * supplicant to actually be scanning. When the network state changes to
- * something other than scanning, we reset this to 0.
- */
- private int mNumScansSinceNetworkStateChange;
-
- // Held during driver load and unload
- private static PowerManager.WakeLock sWakeLock;
+ private NetworkInfo mNetworkInfo;
/* For sending events to connectivity service handler */
private Handler mCsHandler;
private Context mContext;
-
- private DhcpInfo mDhcpInfo;
- private WifiInfo mWifiInfo;
- private NetworkInfo mNetworkInfo;
- private SupplicantStateTracker mSupplicantStateTracker;
-
- // Event log tags (must be in sync with event-log-tags)
- private static final int EVENTLOG_WIFI_STATE_CHANGED = 50021;
- private static final int EVENTLOG_WIFI_EVENT_HANDLED = 50022;
- private static final int EVENTLOG_SUPPLICANT_STATE_CHANGED = 50023;
-
- /* Load the driver */
- private static final int CMD_LOAD_DRIVER = 1;
- /* Unload the driver */
- private static final int CMD_UNLOAD_DRIVER = 2;
- /* Indicates driver load succeeded */
- private static final int CMD_LOAD_DRIVER_SUCCESS = 3;
- /* Indicates driver load failed */
- private static final int CMD_LOAD_DRIVER_FAILURE = 4;
- /* Indicates driver unload succeeded */
- private static final int CMD_UNLOAD_DRIVER_SUCCESS = 5;
- /* Indicates driver unload failed */
- private static final int CMD_UNLOAD_DRIVER_FAILURE = 6;
-
- /* Start the supplicant */
- private static final int CMD_START_SUPPLICANT = 11;
- /* Stop the supplicant */
- private static final int CMD_STOP_SUPPLICANT = 12;
- /* Start the driver */
- private static final int CMD_START_DRIVER = 13;
- /* Start the driver */
- private static final int CMD_STOP_DRIVER = 14;
- /* Indicates DHCP succeded */
- private static final int CMD_IP_CONFIG_SUCCESS = 15;
- /* Indicates DHCP failed */
- private static final int CMD_IP_CONFIG_FAILURE = 16;
- /* Re-configure interface */
- private static final int CMD_RECONFIGURE_IP = 17;
-
-
- /* Start the soft access point */
- private static final int CMD_START_AP = 21;
- /* Stop the soft access point */
- private static final int CMD_STOP_AP = 22;
-
-
- /* Supplicant events */
- /* Connection to supplicant established */
- private static final int SUP_CONNECTION_EVENT = 31;
- /* Connection to supplicant lost */
- private static final int SUP_DISCONNECTION_EVENT = 32;
- /* Driver start completed */
- private static final int DRIVER_START_EVENT = 33;
- /* Driver stop completed */
- private static final int DRIVER_STOP_EVENT = 34;
- /* Network connection completed */
- private static final int NETWORK_CONNECTION_EVENT = 36;
- /* Network disconnection completed */
- private static final int NETWORK_DISCONNECTION_EVENT = 37;
- /* Scan results are available */
- private static final int SCAN_RESULTS_EVENT = 38;
- /* Supplicate state changed */
- private static final int SUPPLICANT_STATE_CHANGE_EVENT = 39;
- /* Password may be incorrect */
- private static final int PASSWORD_MAY_BE_INCORRECT_EVENT = 40;
-
- /* Supplicant commands */
- /* Is supplicant alive ? */
- private static final int CMD_PING_SUPPLICANT = 51;
- /* Add/update a network configuration */
- private static final int CMD_ADD_OR_UPDATE_NETWORK = 52;
- /* Delete a network */
- private static final int CMD_REMOVE_NETWORK = 53;
- /* Enable a network. The device will attempt a connection to the given network. */
- private static final int CMD_ENABLE_NETWORK = 54;
- /* Disable a network. The device does not attempt a connection to the given network. */
- private static final int CMD_DISABLE_NETWORK = 55;
- /* Blacklist network. De-prioritizes the given BSSID for connection. */
- private static final int CMD_BLACKLIST_NETWORK = 56;
- /* Clear the blacklist network list */
- private static final int CMD_CLEAR_BLACKLIST = 57;
- /* Get the configured networks */
- private static final int CMD_GET_NETWORK_CONFIG = 58;
- /* Save configuration */
- private static final int CMD_SAVE_CONFIG = 59;
- /* Connection status */
- private static final int CMD_CONNECTION_STATUS = 60;
-
- /* Supplicant commands after driver start*/
- /* Initiate a scan */
- private static final int CMD_START_SCAN = 71;
- /* Set scan mode. CONNECT_MODE or SCAN_ONLY_MODE */
- private static final int CMD_SET_SCAN_MODE = 72;
- /* Set scan type. SCAN_ACTIVE or SCAN_PASSIVE */
- private static final int CMD_SET_SCAN_TYPE = 73;
- /* Disconnect from a network */
- private static final int CMD_DISCONNECT = 74;
- /* Reconnect to a network */
- private static final int CMD_RECONNECT = 75;
- /* Reassociate to a network */
- private static final int CMD_REASSOCIATE = 76;
- /* Set power mode
- * POWER_MODE_ACTIVE
- * POWER_MODE_AUTO
- */
- private static final int CMD_SET_POWER_MODE = 77;
- /* Set bluetooth co-existence
- * BLUETOOTH_COEXISTENCE_MODE_ENABLED
- * BLUETOOTH_COEXISTENCE_MODE_DISABLED
- * BLUETOOTH_COEXISTENCE_MODE_SENSE
- */
- private static final int CMD_SET_BLUETOOTH_COEXISTENCE = 78;
- /* Enable/disable bluetooth scan mode
- * true(1)
- * false(0)
- */
- private static final int CMD_SET_BLUETOOTH_SCAN_MODE = 79;
- /* Set number of allowed channels */
- private static final int CMD_SET_NUM_ALLOWED_CHANNELS = 80;
- /* Request connectivity manager wake lock before driver stop */
- private static final int CMD_REQUEST_CM_WAKELOCK = 81;
- /* Enables RSSI poll */
- private static final int CMD_ENABLE_RSSI_POLL = 82;
- /* RSSI poll */
- private static final int CMD_RSSI_POLL = 83;
- /* Get current RSSI */
- private static final int CMD_GET_RSSI = 84;
- /* Get approx current RSSI */
- private static final int CMD_GET_RSSI_APPROX = 85;
- /* Get link speed on connection */
- private static final int CMD_GET_LINK_SPEED = 86;
- /* Radio mac address */
- private static final int CMD_GET_MAC_ADDR = 87;
- /* Set up packet filtering */
- private static final int CMD_START_PACKET_FILTERING = 88;
- /* Clear packet filter */
- private static final int CMD_STOP_PACKET_FILTERING = 89;
-
- /* Connectivity service commands */
- /* Bring down wifi connection */
- private static final int CM_CMD_TEARDOWN = 110;
- /* Reconnect to wifi */
- private static final int CM_CMD_RECONNECT = 111;
-
- /**
- * Interval in milliseconds between polling for connection
- * status items that are not sent via asynchronous events.
- * An example is RSSI (signal strength).
- */
- private static final int POLL_RSSI_INTERVAL_MSECS = 3000;
-
- private static final int CONNECT_MODE = 1;
- private static final int SCAN_ONLY_MODE = 2;
-
- private static final int SCAN_ACTIVE = 1;
- private static final int SCAN_PASSIVE = 2;
-
- /**
- * The maximum number of times we will retry a connection to an access point
- * for which we have failed in acquiring an IP address from DHCP. A value of
- * N means that we will make N+1 connection attempts in all.
- * <p>
- * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
- * value if a Settings value is not present.
- */
- private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
-
- private static final int DRIVER_POWER_MODE_ACTIVE = 1;
- private static final int DRIVER_POWER_MODE_AUTO = 0;
-
- /* Default parent state */
- private HierarchicalState mDefaultState = new DefaultState();
- /* Temporary initial state */
- private HierarchicalState mInitialState = new InitialState();
- /* Unloading the driver */
- private HierarchicalState mDriverUnloadingState = new DriverUnloadingState();
- /* Loading the driver */
- private HierarchicalState mDriverUnloadedState = new DriverUnloadedState();
- /* Driver load/unload failed */
- private HierarchicalState mDriverFailedState = new DriverFailedState();
- /* Driver loading */
- private HierarchicalState mDriverLoadingState = new DriverLoadingState();
- /* Driver loaded */
- private HierarchicalState mDriverLoadedState = new DriverLoadedState();
- /* Driver loaded, waiting for supplicant to start */
- private HierarchicalState mWaitForSupState = new WaitForSupState();
-
- /* Driver loaded and supplicant ready */
- private HierarchicalState mDriverSupReadyState = new DriverSupReadyState();
- /* Driver start issued, waiting for completed event */
- private HierarchicalState mDriverStartingState = new DriverStartingState();
- /* Driver started */
- private HierarchicalState mDriverStartedState = new DriverStartedState();
- /* Driver stopping */
- private HierarchicalState mDriverStoppingState = new DriverStoppingState();
- /* Driver stopped */
- private HierarchicalState mDriverStoppedState = new DriverStoppedState();
- /* Scan for networks, no connection will be established */
- private HierarchicalState mScanModeState = new ScanModeState();
- /* Connecting to an access point */
- private HierarchicalState mConnectModeState = new ConnectModeState();
- /* Fetching IP after network connection (assoc+auth complete) */
- private HierarchicalState mConnectingState = new ConnectingState();
- /* Connected with IP addr */
- private HierarchicalState mConnectedState = new ConnectedState();
- /* disconnect issued, waiting for network disconnect confirmation */
- private HierarchicalState mDisconnectingState = new DisconnectingState();
- /* Network is not connected, supplicant assoc+auth is not complete */
- private HierarchicalState mDisconnectedState = new DisconnectedState();
-
- /* Soft Ap is running */
- private HierarchicalState mSoftApStartedState = new SoftApStartedState();
-
- /* Argument for Message object to indicate a synchronous call */
- private static final int SYNCHRONOUS_CALL = 1;
- private static final int ASYNCHRONOUS_CALL = 0;
-
-
- /**
- * One of {@link WifiManager#WIFI_STATE_DISABLED},
- * {@link WifiManager#WIFI_STATE_DISABLING},
- * {@link WifiManager#WIFI_STATE_ENABLED},
- * {@link WifiManager#WIFI_STATE_ENABLING},
- * {@link WifiManager#WIFI_STATE_UNKNOWN}
- *
- */
- private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_DISABLED);
-
- /**
- * One of {@link WifiManager#WIFI_AP_STATE_DISABLED},
- * {@link WifiManager#WIFI_AP_STATE_DISABLING},
- * {@link WifiManager#WIFI_AP_STATE_ENABLED},
- * {@link WifiManager#WIFI_AP_STATE_ENABLING},
- * {@link WifiManager#WIFI_AP_STATE_FAILED}
- *
- */
- private final AtomicInteger mWifiApState = new AtomicInteger(WIFI_AP_STATE_DISABLED);
-
- private final AtomicInteger mLastEnableUid = new AtomicInteger(Process.myUid());
- private final AtomicInteger mLastApEnableUid = new AtomicInteger(Process.myUid());
-
- private final IBatteryStats mBatteryStats;
+ private BroadcastReceiver mWifiStateReceiver;
+ private WifiManager mWifiManager;
public WifiStateTracker(Context context, Handler target) {
- super(NETWORKTYPE);
-
mCsHandler = target;
mContext = context;
mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
- mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
-
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- nwService = INetworkManagementService.Stub.asInterface(b);
-
- mWifiMonitor = new WifiMonitor(this);
- mDhcpInfo = new DhcpInfo();
- mWifiInfo = new WifiInfo();
- mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0");
- mSupplicantStateTracker = new SupplicantStateTracker(context, getHandler());
-
- mBluetoothHeadset = new BluetoothHeadset(mContext, null);
mNetworkProperties = new NetworkProperties();
mNetworkInfo.setIsAvailable(false);
mNetworkProperties.clear();
- resetNotificationTimer();
setTeardownRequested(false);
- mLastBssid = null;
- mLastNetworkId = -1;
- mLastSignalLevel = -1;
-
- mScanResultCache = new LinkedHashMap<String, ScanResult>(
- SCAN_RESULT_CACHE_SIZE, 0.75f, true) {
- /*
- * Limit the cache size by SCAN_RESULT_CACHE_SIZE
- * elements
- */
- @Override
- public boolean removeEldestEntry(Map.Entry eldest) {
- return SCAN_RESULT_CACHE_SIZE < this.size();
- }
- };
-
- // Setting is in seconds
- NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(),
- Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l;
- mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(target);
- mNotificationEnabledSettingObserver.register();
-
- mSettingsObserver = new SettingsObserver(new Handler());
-
- PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
- sWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
-
- addState(mDefaultState);
- addState(mInitialState, mDefaultState);
- addState(mDriverUnloadingState, mDefaultState);
- addState(mDriverUnloadedState, mDefaultState);
- addState(mDriverFailedState, mDriverUnloadedState);
- addState(mDriverLoadingState, mDefaultState);
- addState(mDriverLoadedState, mDefaultState);
- addState(mWaitForSupState, mDriverLoadedState);
- addState(mDriverSupReadyState, mDefaultState);
- addState(mDriverStartingState, mDriverSupReadyState);
- addState(mDriverStartedState, mDriverSupReadyState);
- addState(mScanModeState, mDriverStartedState);
- addState(mConnectModeState, mDriverStartedState);
- addState(mConnectingState, mConnectModeState);
- addState(mConnectedState, mConnectModeState);
- addState(mDisconnectingState, mConnectModeState);
- addState(mDisconnectedState, mConnectModeState);
- addState(mDriverStoppingState, mDriverSupReadyState);
- addState(mDriverStoppedState, mDriverSupReadyState);
- addState(mSoftApStartedState, mDefaultState);
-
- setInitialState(mInitialState);
-
- if (DBG) setDbg(true);
-
- //start the state machine
- start();
}
- /*********************************************************
- * NetworkStateTracker interface implementation
- ********************************************************/
-
public void setTeardownRequested(boolean isRequested) {
mTeardownRequested.set(isRequested);
}
@@ -544,11 +79,17 @@
/**
* Begin monitoring wifi connectivity
- * @deprecated
- *
- * TODO: remove this from callers
*/
public void startMonitoring() {
+ mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.CONFIG_CHANGED_ACTION);
+ filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+
+ mWifiStateReceiver = new WifiStateReceiver();
+ mContext.registerReceiver(mWifiStateReceiver, filter);
}
/**
@@ -556,7 +97,8 @@
* TODO: do away with return value after making MobileDataStateTracker async
*/
public boolean teardown() {
- sendMessage(CM_CMD_TEARDOWN);
+ mTeardownRequested.set(true);
+ mWifiManager.stopWifi();
return true;
}
@@ -565,7 +107,8 @@
* TODO: do away with return value after making MobileDataStateTracker async
*/
public boolean reconnect() {
- sendMessage(CM_CMD_RECONNECT);
+ mTeardownRequested.set(false);
+ mWifiManager.startWifi();
return true;
}
@@ -575,7 +118,7 @@
* TODO: do away with return value after making MobileDataStateTracker async
*/
public boolean setRadio(boolean turnOn) {
- setWifiEnabled(turnOn);
+ mWifiManager.setWifiEnabled(turnOn);
return true;
}
@@ -626,21 +169,6 @@
return -1;
}
- /* TODO: will go away.
- * Notifications are directly handled in WifiStateTracker at checkAndSetNotification()
- */
- public void interpretScanResultsAvailable() {
-
- }
-
- /**
- * Return the name of our WLAN network interface.
- * @return the name of our interface.
- */
- public String getInterfaceName() {
- return mInterfaceName;
- }
-
/**
* Check if private DNS route is set for the network
*/
@@ -698,3279 +226,23 @@
return "net.tcp.buffersize.wifi";
}
-
- /*********************************************************
- * Methods exposed for public use
- ********************************************************/
-
- /**
- * TODO: doc
- */
- public boolean pingSupplicant() {
- return sendSyncMessage(CMD_PING_SUPPLICANT).boolValue;
- }
-
- /**
- * TODO: doc
- */
- public boolean startScan(boolean forceActive) {
- return sendSyncMessage(obtainMessage(CMD_START_SCAN, forceActive ?
- SCAN_ACTIVE : SCAN_PASSIVE, 0)).boolValue;
- }
-
- /**
- * TODO: doc
- */
- public void setWifiEnabled(boolean enable) {
- mLastEnableUid.set(Binder.getCallingUid());
- if (enable) {
- /* Argument is the state that is entered prior to load */
- sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_STATE_ENABLING, 0));
- sendMessage(CMD_START_SUPPLICANT);
- } else {
- sendMessage(CMD_STOP_SUPPLICANT);
- /* Argument is the state that is entered upon success */
- sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_DISABLED, 0));
- }
- }
-
- /**
- * TODO: doc
- */
- public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enable) {
- mLastApEnableUid.set(Binder.getCallingUid());
- if (enable) {
- /* Argument is the state that is entered prior to load */
- sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_AP_STATE_ENABLING, 0));
- sendMessage(obtainMessage(CMD_START_AP, wifiConfig));
- } else {
- sendMessage(CMD_STOP_AP);
- /* Argument is the state that is entered upon success */
- sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_DISABLED, 0));
- }
- }
-
- /**
- * TODO: doc
- */
- public int getWifiState() {
- return mWifiState.get();
- }
-
- /**
- * TODO: doc
- */
- public String getWifiStateByName() {
- switch (mWifiState.get()) {
- case WIFI_STATE_DISABLING:
- return "disabling";
- case WIFI_STATE_DISABLED:
- return "disabled";
- case WIFI_STATE_ENABLING:
- return "enabling";
- case WIFI_STATE_ENABLED:
- return "enabled";
- case WIFI_STATE_UNKNOWN:
- return "unknown state";
- default:
- return "[invalid state]";
- }
- }
-
- /**
- * TODO: doc
- */
- public int getWifiApState() {
- return mWifiApState.get();
- }
-
- /**
- * TODO: doc
- */
- public String getWifiApStateByName() {
- switch (mWifiApState.get()) {
- case WIFI_AP_STATE_DISABLING:
- return "disabling";
- case WIFI_AP_STATE_DISABLED:
- return "disabled";
- case WIFI_AP_STATE_ENABLING:
- return "enabling";
- case WIFI_AP_STATE_ENABLED:
- return "enabled";
- case WIFI_AP_STATE_FAILED:
- return "failed";
- default:
- return "[invalid state]";
- }
- }
-
- /**
- * Get status information for the current connection, if any.
- * @return a {@link WifiInfo} object containing information about the current connection
- *
- */
- public WifiInfo requestConnectionInfo() {
- return mWifiInfo;
- }
-
- public DhcpInfo getDhcpInfo() {
- return mDhcpInfo;
- }
-
- /**
- * TODO: doc
- */
- public void startWifi(boolean enable) {
- if (enable) {
- sendMessage(CMD_START_DRIVER);
- } else {
- sendMessage(CMD_STOP_DRIVER);
- }
- }
-
- /**
- * TODO: doc
- */
- public void disconnectAndStop() {
- sendMessage(CMD_DISCONNECT);
- sendMessage(CMD_STOP_DRIVER);
- }
-
- /**
- * TODO: doc
- */
- public void setScanOnlyMode(boolean enable) {
- if (enable) {
- sendMessage(obtainMessage(CMD_SET_SCAN_MODE, SCAN_ONLY_MODE, 0));
- } else {
- sendMessage(obtainMessage(CMD_SET_SCAN_MODE, CONNECT_MODE, 0));
- }
- }
-
- /**
- * TODO: doc
- */
- public void setScanType(boolean active) {
- if (active) {
- sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_ACTIVE, 0));
- } else {
- sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_PASSIVE, 0));
- }
- }
-
- /**
- * TODO: doc
- */
- public List<ScanResult> getScanResultsList() {
- return mScanResults;
- }
-
- /**
- * Disconnect from Access Point
- */
- public boolean disconnectCommand() {
- return sendSyncMessage(CMD_DISCONNECT).boolValue;
- }
-
- /**
- * Initiate a reconnection to AP
- */
- public boolean reconnectCommand() {
- return sendSyncMessage(CMD_RECONNECT).boolValue;
- }
-
- /**
- * Initiate a re-association to AP
- */
- public boolean reassociateCommand() {
- return sendSyncMessage(CMD_REASSOCIATE).boolValue;
- }
-
- /**
- * Add a network synchronously
- *
- * @return network id of the new network
- */
- public int addOrUpdateNetwork(WifiConfiguration config) {
- return sendSyncMessage(CMD_ADD_OR_UPDATE_NETWORK, config).intValue;
- }
-
- public List<WifiConfiguration> getConfiguredNetworks() {
- return sendSyncMessage(CMD_GET_NETWORK_CONFIG).configList;
- }
-
- /**
- * Delete a network
- *
- * @param networkId id of the network to be removed
- */
- public boolean removeNetwork(int networkId) {
- return sendSyncMessage(obtainMessage(CMD_REMOVE_NETWORK, networkId, 0)).boolValue;
- }
-
- private class EnableNetParams {
- private int netId;
- private boolean disableOthers;
- EnableNetParams(int n, boolean b) {
- netId = n;
- disableOthers = b;
- }
- }
- /**
- * Enable a network
- *
- * @param netId network id of the network
- * @param disableOthers true, if all other networks have to be disabled
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public boolean enableNetwork(int netId, boolean disableOthers) {
- return sendSyncMessage(CMD_ENABLE_NETWORK,
- new EnableNetParams(netId, disableOthers)).boolValue;
- }
-
- /**
- * Disable a network
- *
- * @param netId network id of the network
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public boolean disableNetwork(int netId) {
- return sendSyncMessage(obtainMessage(CMD_DISABLE_NETWORK, netId, 0)).boolValue;
- }
-
- /**
- * Blacklist a BSSID. This will avoid the AP if there are
- * alternate APs to connect
- *
- * @param bssid BSSID of the network
- */
- public void addToBlacklist(String bssid) {
- sendMessage(obtainMessage(CMD_BLACKLIST_NETWORK, bssid));
- }
-
- /**
- * Clear the blacklist list
- *
- */
- public void clearBlacklist() {
- sendMessage(obtainMessage(CMD_CLEAR_BLACKLIST));
- }
-
- /**
- * Get detailed status of the connection
- *
- * @return Example status result
- * bssid=aa:bb:cc:dd:ee:ff
- * ssid=TestNet
- * id=3
- * pairwise_cipher=NONE
- * group_cipher=NONE
- * key_mgmt=NONE
- * wpa_state=COMPLETED
- * ip_address=X.X.X.X
- */
- public String status() {
- return sendSyncMessage(CMD_CONNECTION_STATUS).stringValue;
- }
-
- public void enableRssiPolling(boolean enabled) {
- sendMessage(obtainMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0));
- }
- /**
- * Get RSSI to currently connected network
- *
- * @return RSSI value, -1 on failure
- */
- public int getRssi() {
- return sendSyncMessage(CMD_GET_RSSI).intValue;
- }
-
- /**
- * Get approx RSSI to currently connected network
- *
- * @return RSSI value, -1 on failure
- */
- public int getRssiApprox() {
- return sendSyncMessage(CMD_GET_RSSI_APPROX).intValue;
- }
-
- /**
- * Get link speed to currently connected network
- *
- * @return link speed, -1 on failure
- */
- public int getLinkSpeed() {
- return sendSyncMessage(CMD_GET_LINK_SPEED).intValue;
- }
-
- /**
- * Get MAC address of radio
- *
- * @return MAC address, null on failure
- */
- public String getMacAddress() {
- return sendSyncMessage(CMD_GET_MAC_ADDR).stringValue;
- }
-
- /**
- * Start packet filtering
- */
- public void startPacketFiltering() {
- sendMessage(CMD_START_PACKET_FILTERING);
- }
-
- /**
- * Stop packet filtering
- */
- public void stopPacketFiltering() {
- sendMessage(CMD_STOP_PACKET_FILTERING);
- }
-
- /**
- * Set power mode
- * @param mode
- * DRIVER_POWER_MODE_AUTO
- * DRIVER_POWER_MODE_ACTIVE
- */
- public void setPowerMode(int mode) {
- sendMessage(obtainMessage(CMD_SET_POWER_MODE, mode, 0));
- }
-
- /**
- * Set the number of allowed radio frequency channels from the system
- * setting value, if any.
- */
- public void setNumAllowedChannels() {
- try {
- setNumAllowedChannels(
- Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS));
- } catch (Settings.SettingNotFoundException e) {
- if (mNumAllowedChannels != 0) {
- setNumAllowedChannels(mNumAllowedChannels);
- }
- // otherwise, use the driver default
- }
- }
-
- /**
- * Set the number of radio frequency channels that are allowed to be used
- * in the current regulatory domain.
- * @param numChannels the number of allowed channels. Must be greater than 0
- * and less than or equal to 16.
- */
- public void setNumAllowedChannels(int numChannels) {
- sendMessage(obtainMessage(CMD_SET_NUM_ALLOWED_CHANNELS, numChannels, 0));
- }
-
- /**
- * Get number of allowed channels
- *
- * @return channel count, -1 on failure
- *
- * TODO: this is not a public API and needs to be removed in favor
- * of asynchronous reporting. unused for now.
- */
- public int getNumAllowedChannels() {
- return -1;
- }
-
- /**
- * Set bluetooth coex mode:
- *
- * @param mode
- * BLUETOOTH_COEXISTENCE_MODE_ENABLED
- * BLUETOOTH_COEXISTENCE_MODE_DISABLED
- * BLUETOOTH_COEXISTENCE_MODE_SENSE
- */
- public void setBluetoothCoexistenceMode(int mode) {
- sendMessage(obtainMessage(CMD_SET_BLUETOOTH_COEXISTENCE, mode, 0));
- }
-
- /**
- * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
- * some of the low-level scan parameters used by the driver are changed to
- * reduce interference with A2DP streaming.
- *
- * @param isBluetoothPlaying whether to enable or disable this mode
- */
- public void setBluetoothScanMode(boolean isBluetoothPlaying) {
- sendMessage(obtainMessage(CMD_SET_BLUETOOTH_SCAN_MODE, isBluetoothPlaying ? 1 : 0, 0));
- }
-
- /**
- * Save configuration on supplicant
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- *
- * TODO: deprecate this
- */
- public boolean saveConfig() {
- return sendSyncMessage(CMD_SAVE_CONFIG).boolValue;
- }
-
- /**
- * TODO: doc
- */
- public void requestCmWakeLock() {
- sendMessage(CMD_REQUEST_CM_WAKELOCK);
- }
-
- /*********************************************************
- * Internal private functions
- ********************************************************/
-
- class SyncReturn {
- boolean boolValue;
- int intValue;
- String stringValue;
- Object objValue;
- List<WifiConfiguration> configList;
- }
-
- class SyncParams {
- Object mParameter;
- SyncReturn mSyncReturn;
- SyncParams() {
- mSyncReturn = new SyncReturn();
- }
- SyncParams(Object p) {
- mParameter = p;
- mSyncReturn = new SyncReturn();
- }
- }
-
- /**
- * message.arg2 is reserved to indicate synchronized
- * message.obj is used to store SyncParams
- */
- private SyncReturn syncedSend(Message msg) {
- SyncParams syncParams = (SyncParams) msg.obj;
- msg.arg2 = SYNCHRONOUS_CALL;
- synchronized(syncParams) {
- if (DBG) Log.d(TAG, "syncedSend " + msg);
- sendMessage(msg);
- try {
- syncParams.wait();
- } catch (InterruptedException e) {
- Log.e(TAG, "sendSyncMessage: unexpected interruption of wait()");
- return null;
- }
- }
- return syncParams.mSyncReturn;
- }
-
- private SyncReturn sendSyncMessage(Message msg) {
- SyncParams syncParams = new SyncParams();
- msg.obj = syncParams;
- return syncedSend(msg);
- }
-
- private SyncReturn sendSyncMessage(int what, Object param) {
- SyncParams syncParams = new SyncParams(param);
- Message msg = obtainMessage(what, syncParams);
- return syncedSend(msg);
- }
-
-
- private SyncReturn sendSyncMessage(int what) {
- return sendSyncMessage(obtainMessage(what));
- }
-
- private void notifyOnMsgObject(Message msg) {
- SyncParams syncParams = (SyncParams) msg.obj;
- if (syncParams != null) {
- synchronized(syncParams) {
- if (DBG) Log.d(TAG, "notifyOnMsgObject " + msg);
- syncParams.notify();
- }
- }
- else {
- Log.e(TAG, "Error! syncParams in notifyOnMsgObject is null");
- }
- }
-
- private void setWifiState(int wifiState) {
- final int previousWifiState = mWifiState.get();
-
- try {
- if (wifiState == WIFI_STATE_ENABLED) {
- mBatteryStats.noteWifiOn(mLastEnableUid.get());
- } else if (wifiState == WIFI_STATE_DISABLED) {
- mBatteryStats.noteWifiOff(mLastEnableUid.get());
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to note battery stats in wifi");
- }
-
- mWifiState.set(wifiState);
-
- if (DBG) Log.d(TAG, "setWifiState: " + getWifiStateByName());
-
- final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
- intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
- mContext.sendStickyBroadcast(intent);
- }
-
- private void setWifiApState(int wifiApState) {
- final int previousWifiApState = mWifiApState.get();
-
- try {
- if (wifiApState == WIFI_AP_STATE_ENABLED) {
- mBatteryStats.noteWifiOn(mLastApEnableUid.get());
- } else if (wifiApState == WIFI_AP_STATE_DISABLED) {
- mBatteryStats.noteWifiOff(mLastApEnableUid.get());
- }
- } catch (RemoteException e) {
- Log.d(TAG, "Failed to note battery stats in wifi");
- }
-
- // Update state
- mWifiApState.set(wifiApState);
-
- if (DBG) Log.d(TAG, "setWifiApState: " + getWifiApStateByName());
-
- // Broadcast
- final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState);
- intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
- mContext.sendStickyBroadcast(intent);
- }
-
- /**
- * Parse the scan result line passed to us by wpa_supplicant (helper).
- * @param line the line to parse
- * @return the {@link ScanResult} object
- */
- private ScanResult parseScanResult(String line) {
- ScanResult scanResult = null;
- if (line != null) {
- /*
- * Cache implementation (LinkedHashMap) is not synchronized, thus,
- * must synchronized here!
- */
- synchronized (mScanResultCache) {
- String[] result = scanResultPattern.split(line);
- if (3 <= result.length && result.length <= 5) {
- String bssid = result[0];
- // bssid | frequency | level | flags | ssid
- int frequency;
- int level;
- try {
- frequency = Integer.parseInt(result[1]);
- level = Integer.parseInt(result[2]);
- /* some implementations avoid negative values by adding 256
- * so we need to adjust for that here.
- */
- if (level > 0) level -= 256;
- } catch (NumberFormatException e) {
- frequency = 0;
- level = 0;
- }
-
- /*
- * The formatting of the results returned by
- * wpa_supplicant is intended to make the fields
- * line up nicely when printed,
- * not to make them easy to parse. So we have to
- * apply some heuristics to figure out which field
- * is the SSID and which field is the flags.
- */
- String ssid;
- String flags;
- if (result.length == 4) {
- if (result[3].charAt(0) == '[') {
- flags = result[3];
- ssid = "";
- } else {
- flags = "";
- ssid = result[3];
- }
- } else if (result.length == 5) {
- flags = result[3];
- ssid = result[4];
- } else {
- // Here, we must have 3 fields: no flags and ssid
- // set
- flags = "";
- ssid = "";
- }
-
- // bssid + ssid is the hash key
- String key = bssid + ssid;
- scanResult = mScanResultCache.get(key);
- if (scanResult != null) {
- scanResult.level = level;
- scanResult.SSID = ssid;
- scanResult.capabilities = flags;
- scanResult.frequency = frequency;
- } else {
- // Do not add scan results that have no SSID set
- if (0 < ssid.trim().length()) {
- scanResult =
- new ScanResult(
- ssid, bssid, flags, level, frequency);
- mScanResultCache.put(key, scanResult);
- }
- }
- } else {
- Log.w(TAG, "Misformatted scan result text with " +
- result.length + " fields: " + line);
- }
- }
- }
-
- return scanResult;
- }
-
- /**
- * scanResults input format
- * 00:bb:cc:dd:cc:ee 2427 166 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net1
- * 00:bb:cc:dd:cc:ff 2412 165 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net2
- */
- private void setScanResults(String scanResults) {
- if (scanResults == null) {
- return;
- }
-
- List<ScanResult> scanList = new ArrayList<ScanResult>();
-
- int lineCount = 0;
-
- int scanResultsLen = scanResults.length();
- // Parse the result string, keeping in mind that the last line does
- // not end with a newline.
- for (int lineBeg = 0, lineEnd = 0; lineEnd <= scanResultsLen; ++lineEnd) {
- if (lineEnd == scanResultsLen || scanResults.charAt(lineEnd) == '\n') {
- ++lineCount;
-
- if (lineCount == 1) {
- lineBeg = lineEnd + 1;
- continue;
- }
- if (lineEnd > lineBeg) {
- String line = scanResults.substring(lineBeg, lineEnd);
- ScanResult scanResult = parseScanResult(line);
- if (scanResult != null) {
- scanList.add(scanResult);
- } else {
- Log.w(TAG, "misformatted scan result for: " + line);
- }
- }
- lineBeg = lineEnd + 1;
- }
- }
-
- mScanResults = scanList;
- }
-
- private void checkAndSetNotification() {
- // If we shouldn't place a notification on available networks, then
- // don't bother doing any of the following
- if (!mNotificationEnabled) return;
-
- State state = mNetworkInfo.getState();
- if ((state == NetworkInfo.State.DISCONNECTED)
- || (state == NetworkInfo.State.UNKNOWN)) {
- // Look for an open network
- List<ScanResult> scanResults = mScanResults;
- if (scanResults != null) {
- int numOpenNetworks = 0;
- for (int i = scanResults.size() - 1; i >= 0; i--) {
- ScanResult scanResult = scanResults.get(i);
-
- if (TextUtils.isEmpty(scanResult.capabilities)) {
- numOpenNetworks++;
- }
- }
-
- if (numOpenNetworks > 0) {
- if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) {
- /*
- * We've scanned continuously at least
- * NUM_SCANS_BEFORE_NOTIFICATION times. The user
- * probably does not have a remembered network in range,
- * since otherwise supplicant would have tried to
- * associate and thus resetting this counter.
- */
- setNotificationVisible(true, numOpenNetworks, false, 0);
- }
- return;
- }
- }
- }
-
- // No open networks in range, remove the notification
- setNotificationVisible(false, 0, false, 0);
- }
-
- /**
- * Display or don't display a notification that there are open Wi-Fi networks.
- * @param visible {@code true} if notification should be visible, {@code false} otherwise
- * @param numNetworks the number networks seen
- * @param force {@code true} to force notification to be shown/not-shown,
- * even if it is already shown/not-shown.
- * @param delay time in milliseconds after which the notification should be made
- * visible or invisible.
- */
- private void setNotificationVisible(boolean visible, int numNetworks, boolean force,
- int delay) {
-
- // Since we use auto cancel on the notification, when the
- // mNetworksAvailableNotificationShown is true, the notification may
- // have actually been canceled. However, when it is false we know
- // for sure that it is not being shown (it will not be shown any other
- // place than here)
-
- // If it should be hidden and it is already hidden, then noop
- if (!visible && !mNotificationShown && !force) {
- return;
- }
-
- Message message;
- if (visible) {
-
- // Not enough time has passed to show the notification again
- if (System.currentTimeMillis() < mNotificationRepeatTime) {
- return;
- }
-
- if (mNotification == null) {
- // Cache the Notification mainly so we can remove the
- // EVENT_NOTIFICATION_CHANGED message with this Notification from
- // the queue later
- mNotification = new Notification();
- mNotification.when = 0;
- mNotification.icon = ICON_NETWORKS_AVAILABLE;
- mNotification.flags = Notification.FLAG_AUTO_CANCEL;
- mNotification.contentIntent = PendingIntent.getActivity(mContext, 0,
- new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK), 0);
- }
-
- CharSequence title = mContext.getResources().getQuantityText(
- com.android.internal.R.plurals.wifi_available, numNetworks);
- CharSequence details = mContext.getResources().getQuantityText(
- com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
- mNotification.tickerText = title;
- mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent);
-
- mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS;
-
- message = mCsHandler.obtainMessage(EVENT_NOTIFICATION_CHANGED, 1,
- ICON_NETWORKS_AVAILABLE, mNotification);
-
- } else {
-
- // Remove any pending messages to show the notification
- mCsHandler.removeMessages(EVENT_NOTIFICATION_CHANGED, mNotification);
-
- message = mCsHandler.obtainMessage(EVENT_NOTIFICATION_CHANGED, 0,
- ICON_NETWORKS_AVAILABLE);
- }
-
- mCsHandler.sendMessageDelayed(message, delay);
-
- mNotificationShown = visible;
- }
-
- private void configureNetworkProperties() {
- try {
- mNetworkProperties.setInterface(NetworkInterface.getByName(mInterfaceName));
- } catch (SocketException e) {
- Log.e(TAG, "SocketException creating NetworkInterface from " + mInterfaceName +
- ". e=" + e);
- return;
- } catch (NullPointerException e) {
- Log.e(TAG, "NPE creating NetworkInterface. e=" + e);
- return;
- }
- // TODO - fix this for v6
- try {
- mNetworkProperties.addAddress(InetAddress.getByAddress(
- NetworkUtils.v4IntToArray(mDhcpInfo.ipAddress)));
- } catch (UnknownHostException e) {
- Log.e(TAG, "Exception setting IpAddress using " + mDhcpInfo + ", e=" + e);
- }
-
- try {
- mNetworkProperties.setGateway(InetAddress.getByAddress(NetworkUtils.v4IntToArray(
- mDhcpInfo.gateway)));
- } catch (UnknownHostException e) {
- Log.e(TAG, "Exception setting Gateway using " + mDhcpInfo + ", e=" + e);
- }
-
- try {
- mNetworkProperties.addDns(InetAddress.getByAddress(
- NetworkUtils.v4IntToArray(mDhcpInfo.dns1)));
- } catch (UnknownHostException e) {
- Log.e(TAG, "Exception setting Dns1 using " + mDhcpInfo + ", e=" + e);
- }
- try {
- mNetworkProperties.addDns(InetAddress.getByAddress(
- NetworkUtils.v4IntToArray(mDhcpInfo.dns2)));
-
- } catch (UnknownHostException e) {
- Log.e(TAG, "Exception setting Dns2 using " + mDhcpInfo + ", e=" + e);
- }
- // TODO - add proxy info
- }
-
-
- private void checkUseStaticIp() {
- mUseStaticIp = false;
- final ContentResolver cr = mContext.getContentResolver();
- try {
- if (Settings.System.getInt(cr, Settings.System.WIFI_USE_STATIC_IP) == 0) {
- return;
- }
- } catch (Settings.SettingNotFoundException e) {
- return;
- }
-
- try {
- String addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_IP);
- if (addr != null) {
- mDhcpInfo.ipAddress = stringToIpAddr(addr);
- } else {
- return;
- }
- addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_GATEWAY);
- if (addr != null) {
- mDhcpInfo.gateway = stringToIpAddr(addr);
- } else {
- return;
- }
- addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_NETMASK);
- if (addr != null) {
- mDhcpInfo.netmask = stringToIpAddr(addr);
- } else {
- return;
- }
- addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS1);
- if (addr != null) {
- mDhcpInfo.dns1 = stringToIpAddr(addr);
- } else {
- return;
- }
- addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS2);
- if (addr != null) {
- mDhcpInfo.dns2 = stringToIpAddr(addr);
- } else {
- mDhcpInfo.dns2 = 0;
- }
- } catch (UnknownHostException e) {
- return;
- }
- mUseStaticIp = true;
- }
-
- private static int stringToIpAddr(String addrString) throws UnknownHostException {
- try {
- String[] parts = addrString.split("\\.");
- if (parts.length != 4) {
- throw new UnknownHostException(addrString);
- }
-
- int a = Integer.parseInt(parts[0]) ;
- int b = Integer.parseInt(parts[1]) << 8;
- int c = Integer.parseInt(parts[2]) << 16;
- int d = Integer.parseInt(parts[3]) << 24;
-
- return a | b | c | d;
- } catch (NumberFormatException ex) {
- throw new UnknownHostException(addrString);
- }
- }
-
- private int getMaxDhcpRetries() {
- return Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT,
- DEFAULT_MAX_DHCP_RETRIES);
- }
-
- private class SettingsObserver extends ContentObserver {
- public SettingsObserver(Handler handler) {
- super(handler);
- ContentResolver cr = mContext.getContentResolver();
- cr.registerContentObserver(Settings.System.getUriFor(
- Settings.System.WIFI_USE_STATIC_IP), false, this);
- cr.registerContentObserver(Settings.System.getUriFor(
- Settings.System.WIFI_STATIC_IP), false, this);
- cr.registerContentObserver(Settings.System.getUriFor(
- Settings.System.WIFI_STATIC_GATEWAY), false, this);
- cr.registerContentObserver(Settings.System.getUriFor(
- Settings.System.WIFI_STATIC_NETMASK), false, this);
- cr.registerContentObserver(Settings.System.getUriFor(
- Settings.System.WIFI_STATIC_DNS1), false, this);
- cr.registerContentObserver(Settings.System.getUriFor(
- Settings.System.WIFI_STATIC_DNS2), false, this);
- }
-
+ private class WifiStateReceiver extends BroadcastReceiver {
@Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
-
- boolean wasStaticIp = mUseStaticIp;
- int oIp, oGw, oMsk, oDns1, oDns2;
- oIp = oGw = oMsk = oDns1 = oDns2 = 0;
- if (wasStaticIp) {
- oIp = mDhcpInfo.ipAddress;
- oGw = mDhcpInfo.gateway;
- oMsk = mDhcpInfo.netmask;
- oDns1 = mDhcpInfo.dns1;
- oDns2 = mDhcpInfo.dns2;
- }
- checkUseStaticIp();
-
- if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) {
- return;
- }
-
- boolean changed =
- (wasStaticIp != mUseStaticIp) ||
- (wasStaticIp && (
- oIp != mDhcpInfo.ipAddress ||
- oGw != mDhcpInfo.gateway ||
- oMsk != mDhcpInfo.netmask ||
- oDns1 != mDhcpInfo.dns1 ||
- oDns2 != mDhcpInfo.dns2));
-
- if (changed) {
- sendMessage(CMD_RECONFIGURE_IP);
- if (mUseStaticIp) {
- mCsHandler.sendEmptyMessage(EVENT_CONFIGURATION_CHANGED);
- }
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+ mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
+ WifiManager.EXTRA_NETWORK_INFO);
+ mNetworkProperties = (NetworkProperties) intent.getParcelableExtra(
+ WifiManager.EXTRA_NETWORK_PROPERTIES);
+ Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
+ msg.sendToTarget();
+ } else if (intent.getAction().equals(WifiManager.CONFIG_CHANGED_ACTION)) {
+ mNetworkProperties = (NetworkProperties) intent.getParcelableExtra(
+ WifiManager.EXTRA_NETWORK_PROPERTIES);
+ Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
+ msg.sendToTarget();
}
}
}
-
- /**
- * Clears variables related to tracking whether a notification has been
- * shown recently.
- * <p>
- * After calling this method, the timer that prevents notifications from
- * being shown too often will be cleared.
- */
- private void resetNotificationTimer() {
- mNotificationRepeatTime = 0;
- mNumScansSinceNetworkStateChange = 0;
- }
-
-
- /**
- * Whether to disable coexistence mode while obtaining IP address. This
- * logic will return true only if the current bluetooth
- * headset/handsfree state is disconnected. This means if it is in an
- * error state, we will NOT disable coexistence mode to err on the side
- * of safety.
- *
- * @return Whether to disable coexistence mode.
- */
- private boolean shouldDisableCoexistenceMode() {
- int state = mBluetoothHeadset.getState(mBluetoothHeadset.getCurrentHeadset());
- return state == BluetoothHeadset.STATE_DISCONNECTED;
- }
-
- private void checkIsBluetoothPlaying() {
- boolean isBluetoothPlaying = false;
- Set<BluetoothDevice> connected = mBluetoothA2dp.getConnectedSinks();
-
- for (BluetoothDevice device : connected) {
- if (mBluetoothA2dp.getSinkState(device) == BluetoothA2dp.STATE_PLAYING) {
- isBluetoothPlaying = true;
- break;
- }
- }
- setBluetoothScanMode(isBluetoothPlaying);
- }
-
- private void sendScanResultsAvailableBroadcast() {
- if (!ActivityManagerNative.isSystemReady()) return;
-
- mContext.sendBroadcast(new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
- }
-
- private void sendRssiChangeBroadcast(final int newRssi) {
- if (!ActivityManagerNative.isSystemReady()) return;
-
- Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
- intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
- mContext.sendBroadcast(intent);
- }
-
- private void sendNetworkStateChangeBroadcast(String bssid) {
- Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
- | Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
- if (bssid != null)
- intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
- mContext.sendStickyBroadcast(intent);
- }
-
- private void sendSupplicantStateChangedBroadcast(StateChangeResult sc, boolean failedAuth) {
- Intent intent = new Intent(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
- | Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra(WifiManager.EXTRA_NEW_STATE, (Parcelable)sc.state);
- if (failedAuth) {
- intent.putExtra(
- WifiManager.EXTRA_SUPPLICANT_ERROR,
- WifiManager.ERROR_AUTHENTICATING);
- }
- mContext.sendStickyBroadcast(intent);
- }
-
- private void sendSupplicantConnectionChangedBroadcast(boolean connected) {
- if (!ActivityManagerNative.isSystemReady()) return;
-
- Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
- intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected);
- mContext.sendBroadcast(intent);
- }
-
- /**
- * Record the detailed state of a network, and if it is a
- * change from the previous state, send a notification to
- * any listeners.
- * @param state the new @{code DetailedState}
- */
- private void setDetailedState(NetworkInfo.DetailedState state) {
- Log.d(TAG, "setDetailed state, old ="
- + mNetworkInfo.getDetailedState() + " and new state=" + state);
- if (state != mNetworkInfo.getDetailedState()) {
- mNetworkInfo.setDetailedState(state, null, null);
- Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
- msg.sendToTarget();
- }
- }
-
- private static String removeDoubleQuotes(String string) {
- if (string.length() <= 2) return "";
- return string.substring(1, string.length() - 1);
- }
-
- private static String convertToQuotedString(String string) {
- return "\"" + string + "\"";
- }
-
- private static String makeString(BitSet set, String[] strings) {
- StringBuffer buf = new StringBuffer();
- int nextSetBit = -1;
-
- /* Make sure all set bits are in [0, strings.length) to avoid
- * going out of bounds on strings. (Shouldn't happen, but...) */
- set = set.get(0, strings.length);
-
- while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
- buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
- }
-
- // remove trailing space
- if (set.cardinality() > 0) {
- buf.setLength(buf.length() - 1);
- }
-
- return buf.toString();
- }
-
- private static int lookupString(String string, String[] strings) {
- int size = strings.length;
-
- string = string.replace('-', '_');
-
- for (int i = 0; i < size; i++)
- if (string.equals(strings[i]))
- return i;
-
- // if we ever get here, we should probably add the
- // value to WifiConfiguration to reflect that it's
- // supported by the WPA supplicant
- Log.w(TAG, "Failed to look-up a string: " + string);
-
- return -1;
- }
-
- private int addOrUpdateNetworkNative(WifiConfiguration config) {
- /*
- * If the supplied networkId is -1, we create a new empty
- * network configuration. Otherwise, the networkId should
- * refer to an existing configuration.
- */
- int netId = config.networkId;
- boolean newNetwork = netId == -1;
- // networkId of -1 means we want to create a new network
-
- if (newNetwork) {
- netId = WifiNative.addNetworkCommand();
- if (netId < 0) {
- Log.e(TAG, "Failed to add a network!");
- return -1;
- }
- }
-
- setVariables: {
-
- if (config.SSID != null &&
- !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.ssidVarName,
- config.SSID)) {
- Log.d(TAG, "failed to set SSID: "+config.SSID);
- break setVariables;
- }
-
- if (config.BSSID != null &&
- !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.bssidVarName,
- config.BSSID)) {
- Log.d(TAG, "failed to set BSSID: "+config.BSSID);
- break setVariables;
- }
-
- String allowedKeyManagementString =
- makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
- if (config.allowedKeyManagement.cardinality() != 0 &&
- !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.KeyMgmt.varName,
- allowedKeyManagementString)) {
- Log.d(TAG, "failed to set key_mgmt: "+
- allowedKeyManagementString);
- break setVariables;
- }
-
- String allowedProtocolsString =
- makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
- if (config.allowedProtocols.cardinality() != 0 &&
- !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.Protocol.varName,
- allowedProtocolsString)) {
- Log.d(TAG, "failed to set proto: "+
- allowedProtocolsString);
- break setVariables;
- }
-
- String allowedAuthAlgorithmsString =
- makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
- if (config.allowedAuthAlgorithms.cardinality() != 0 &&
- !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.AuthAlgorithm.varName,
- allowedAuthAlgorithmsString)) {
- Log.d(TAG, "failed to set auth_alg: "+
- allowedAuthAlgorithmsString);
- break setVariables;
- }
-
- String allowedPairwiseCiphersString =
- makeString(config.allowedPairwiseCiphers,
- WifiConfiguration.PairwiseCipher.strings);
- if (config.allowedPairwiseCiphers.cardinality() != 0 &&
- !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.PairwiseCipher.varName,
- allowedPairwiseCiphersString)) {
- Log.d(TAG, "failed to set pairwise: "+
- allowedPairwiseCiphersString);
- break setVariables;
- }
-
- String allowedGroupCiphersString =
- makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
- if (config.allowedGroupCiphers.cardinality() != 0 &&
- !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.GroupCipher.varName,
- allowedGroupCiphersString)) {
- Log.d(TAG, "failed to set group: "+
- allowedGroupCiphersString);
- break setVariables;
- }
-
- // Prevent client screw-up by passing in a WifiConfiguration we gave it
- // by preventing "*" as a key.
- if (config.preSharedKey != null && !config.preSharedKey.equals("*") &&
- !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.pskVarName,
- config.preSharedKey)) {
- Log.d(TAG, "failed to set psk: "+config.preSharedKey);
- break setVariables;
- }
-
- boolean hasSetKey = false;
- if (config.wepKeys != null) {
- for (int i = 0; i < config.wepKeys.length; i++) {
- // Prevent client screw-up by passing in a WifiConfiguration we gave it
- // by preventing "*" as a key.
- if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
- if (!WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.wepKeyVarNames[i],
- config.wepKeys[i])) {
- Log.d(TAG,
- "failed to set wep_key"+i+": " +
- config.wepKeys[i]);
- break setVariables;
- }
- hasSetKey = true;
- }
- }
- }
-
- if (hasSetKey) {
- if (!WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.wepTxKeyIdxVarName,
- Integer.toString(config.wepTxKeyIndex))) {
- Log.d(TAG,
- "failed to set wep_tx_keyidx: "+
- config.wepTxKeyIndex);
- break setVariables;
- }
- }
-
- if (!WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.priorityVarName,
- Integer.toString(config.priority))) {
- Log.d(TAG, config.SSID + ": failed to set priority: "
- +config.priority);
- break setVariables;
- }
-
- if (config.hiddenSSID && !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.hiddenSSIDVarName,
- Integer.toString(config.hiddenSSID ? 1 : 0))) {
- Log.d(TAG, config.SSID + ": failed to set hiddenSSID: "+
- config.hiddenSSID);
- break setVariables;
- }
-
- for (WifiConfiguration.EnterpriseField field
- : config.enterpriseFields) {
- String varName = field.varName();
- String value = field.value();
- if (value != null) {
- if (field != config.eap) {
- value = (value.length() == 0) ? "NULL" : convertToQuotedString(value);
- }
- if (!WifiNative.setNetworkVariableCommand(
- netId,
- varName,
- value)) {
- Log.d(TAG, config.SSID + ": failed to set " + varName +
- ": " + value);
- break setVariables;
- }
- }
- }
- return netId;
- }
-
- if (newNetwork) {
- WifiNative.removeNetworkCommand(netId);
- Log.d(TAG,
- "Failed to set a network variable, removed network: "
- + netId);
- }
-
- return -1;
- }
-
- private List<WifiConfiguration> getConfiguredNetworksNative() {
- String listStr = WifiNative.listNetworksCommand();
-
- List<WifiConfiguration> networks =
- new ArrayList<WifiConfiguration>();
- if (listStr == null)
- return networks;
-
- String[] lines = listStr.split("\n");
- // Skip the first line, which is a header
- for (int i = 1; i < lines.length; i++) {
- String[] result = lines[i].split("\t");
- // network-id | ssid | bssid | flags
- WifiConfiguration config = new WifiConfiguration();
- try {
- config.networkId = Integer.parseInt(result[0]);
- } catch(NumberFormatException e) {
- continue;
- }
- if (result.length > 3) {
- if (result[3].indexOf("[CURRENT]") != -1)
- config.status = WifiConfiguration.Status.CURRENT;
- else if (result[3].indexOf("[DISABLED]") != -1)
- config.status = WifiConfiguration.Status.DISABLED;
- else
- config.status = WifiConfiguration.Status.ENABLED;
- } else {
- config.status = WifiConfiguration.Status.ENABLED;
- }
- readNetworkVariables(config);
- networks.add(config);
- }
- return networks;
- }
-
- /**
- * Read the variables from the supplicant daemon that are needed to
- * fill in the WifiConfiguration object.
- *
- * @param config the {@link WifiConfiguration} object to be filled in.
- */
- private void readNetworkVariables(WifiConfiguration config) {
-
- int netId = config.networkId;
- if (netId < 0)
- return;
-
- /*
- * TODO: maybe should have a native method that takes an array of
- * variable names and returns an array of values. But we'd still
- * be doing a round trip to the supplicant daemon for each variable.
- */
- String value;
-
- value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.ssidVarName);
- if (!TextUtils.isEmpty(value)) {
- config.SSID = removeDoubleQuotes(value);
- } else {
- config.SSID = null;
- }
-
- value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.bssidVarName);
- if (!TextUtils.isEmpty(value)) {
- config.BSSID = value;
- } else {
- config.BSSID = null;
- }
-
- value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName);
- config.priority = -1;
- if (!TextUtils.isEmpty(value)) {
- try {
- config.priority = Integer.parseInt(value);
- } catch (NumberFormatException ignore) {
- }
- }
-
- value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.hiddenSSIDVarName);
- config.hiddenSSID = false;
- if (!TextUtils.isEmpty(value)) {
- try {
- config.hiddenSSID = Integer.parseInt(value) != 0;
- } catch (NumberFormatException ignore) {
- }
- }
-
- value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepTxKeyIdxVarName);
- config.wepTxKeyIndex = -1;
- if (!TextUtils.isEmpty(value)) {
- try {
- config.wepTxKeyIndex = Integer.parseInt(value);
- } catch (NumberFormatException ignore) {
- }
- }
-
- for (int i = 0; i < 4; i++) {
- value = WifiNative.getNetworkVariableCommand(netId,
- WifiConfiguration.wepKeyVarNames[i]);
- if (!TextUtils.isEmpty(value)) {
- config.wepKeys[i] = value;
- } else {
- config.wepKeys[i] = null;
- }
- }
-
- value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.pskVarName);
- if (!TextUtils.isEmpty(value)) {
- config.preSharedKey = value;
- } else {
- config.preSharedKey = null;
- }
-
- value = WifiNative.getNetworkVariableCommand(config.networkId,
- WifiConfiguration.Protocol.varName);
- if (!TextUtils.isEmpty(value)) {
- String vals[] = value.split(" ");
- for (String val : vals) {
- int index =
- lookupString(val, WifiConfiguration.Protocol.strings);
- if (0 <= index) {
- config.allowedProtocols.set(index);
- }
- }
- }
-
- value = WifiNative.getNetworkVariableCommand(config.networkId,
- WifiConfiguration.KeyMgmt.varName);
- if (!TextUtils.isEmpty(value)) {
- String vals[] = value.split(" ");
- for (String val : vals) {
- int index =
- lookupString(val, WifiConfiguration.KeyMgmt.strings);
- if (0 <= index) {
- config.allowedKeyManagement.set(index);
- }
- }
- }
-
- value = WifiNative.getNetworkVariableCommand(config.networkId,
- WifiConfiguration.AuthAlgorithm.varName);
- if (!TextUtils.isEmpty(value)) {
- String vals[] = value.split(" ");
- for (String val : vals) {
- int index =
- lookupString(val, WifiConfiguration.AuthAlgorithm.strings);
- if (0 <= index) {
- config.allowedAuthAlgorithms.set(index);
- }
- }
- }
-
- value = WifiNative.getNetworkVariableCommand(config.networkId,
- WifiConfiguration.PairwiseCipher.varName);
- if (!TextUtils.isEmpty(value)) {
- String vals[] = value.split(" ");
- for (String val : vals) {
- int index =
- lookupString(val, WifiConfiguration.PairwiseCipher.strings);
- if (0 <= index) {
- config.allowedPairwiseCiphers.set(index);
- }
- }
- }
-
- value = WifiNative.getNetworkVariableCommand(config.networkId,
- WifiConfiguration.GroupCipher.varName);
- if (!TextUtils.isEmpty(value)) {
- String vals[] = value.split(" ");
- for (String val : vals) {
- int index =
- lookupString(val, WifiConfiguration.GroupCipher.strings);
- if (0 <= index) {
- config.allowedGroupCiphers.set(index);
- }
- }
- }
-
- for (WifiConfiguration.EnterpriseField field :
- config.enterpriseFields) {
- value = WifiNative.getNetworkVariableCommand(netId,
- field.varName());
- if (!TextUtils.isEmpty(value)) {
- if (field != config.eap) value = removeDoubleQuotes(value);
- field.setValue(value);
- }
- }
-
- }
-
- /**
- * Poll for info not reported via events
- * RSSI & Linkspeed
- */
- private void requestPolledInfo() {
- int newRssi = WifiNative.getRssiCommand();
- if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values
- /* some implementations avoid negative values by adding 256
- * so we need to adjust for that here.
- */
- if (newRssi > 0) newRssi -= 256;
- mWifiInfo.setRssi(newRssi);
- /*
- * Rather then sending the raw RSSI out every time it
- * changes, we precalculate the signal level that would
- * be displayed in the status bar, and only send the
- * broadcast if that much more coarse-grained number
- * changes. This cuts down greatly on the number of
- * broadcasts, at the cost of not mWifiInforming others
- * interested in RSSI of all the changes in signal
- * level.
- */
- // TODO: The second arg to the call below needs to be a symbol somewhere, but
- // it's actually the size of an array of icons that's private
- // to StatusBar Policy.
- int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4);
- if (newSignalLevel != mLastSignalLevel) {
- sendRssiChangeBroadcast(newRssi);
- }
- mLastSignalLevel = newSignalLevel;
- } else {
- mWifiInfo.setRssi(-200);
- }
- int newLinkSpeed = WifiNative.getLinkSpeedCommand();
- if (newLinkSpeed != -1) {
- mWifiInfo.setLinkSpeed(newLinkSpeed);
- }
- }
-
- /**
- * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
- * using the interface, stopping DHCP & disabling interface
- */
- private void handleNetworkDisconnect() {
- Log.d(TAG, "Reset connections and stopping DHCP");
-
- /*
- * Reset connections & stop DHCP
- */
- NetworkUtils.resetConnections(mInterfaceName);
-
- if (!NetworkUtils.stopDhcp(mInterfaceName)) {
- Log.e(TAG, "Could not stop DHCP");
- }
-
- /* Disable interface */
- NetworkUtils.disableInterface(mInterfaceName);
-
- /* send event to CM & network change broadcast */
- setDetailedState(DetailedState.DISCONNECTED);
- sendNetworkStateChangeBroadcast(mLastBssid);
-
- /* Reset data structures */
- mWifiInfo.setIpAddress(0);
- mWifiInfo.setBSSID(null);
- mWifiInfo.setSSID(null);
- mWifiInfo.setNetworkId(-1);
-
- /* Clear network properties */
- mNetworkProperties.clear();
-
- mLastBssid= null;
- mLastNetworkId = -1;
-
- }
-
-
- /*********************************************************
- * Notifications from WifiMonitor
- ********************************************************/
-
- /**
- * A structure for supplying information about a supplicant state
- * change in the STATE_CHANGE event message that comes from the
- * WifiMonitor
- * thread.
- */
- private static class StateChangeResult {
- StateChangeResult(int networkId, String BSSID, Object state) {
- this.state = state;
- this.BSSID = BSSID;
- this.networkId = networkId;
- }
- int networkId;
- String BSSID;
- Object state;
- }
-
- /**
- * Send the tracker a notification that a user-entered password key
- * may be incorrect (i.e., caused authentication to fail).
- */
- void notifyPasswordKeyMayBeIncorrect() {
- sendMessage(PASSWORD_MAY_BE_INCORRECT_EVENT);
- }
-
- /**
- * Send the tracker a notification that a connection to the supplicant
- * daemon has been established.
- */
- void notifySupplicantConnection() {
- sendMessage(SUP_CONNECTION_EVENT);
- }
-
- /**
- * Send the tracker a notification that a connection to the supplicant
- * daemon has been established.
- */
- void notifySupplicantLost() {
- sendMessage(SUP_DISCONNECTION_EVENT);
- }
-
- /**
- * Send the tracker a notification that the state of Wifi connectivity
- * has changed.
- * @param networkId the configured network on which the state change occurred
- * @param newState the new network state
- * @param BSSID when the new state is {@link DetailedState#CONNECTED
- * NetworkInfo.DetailedState.CONNECTED},
- * this is the MAC address of the access point. Otherwise, it
- * is {@code null}.
- */
- void notifyNetworkStateChange(DetailedState newState, String BSSID, int networkId) {
- if (newState == NetworkInfo.DetailedState.CONNECTED) {
- sendMessage(obtainMessage(NETWORK_CONNECTION_EVENT,
- new StateChangeResult(networkId, BSSID, newState)));
- } else {
- sendMessage(obtainMessage(NETWORK_DISCONNECTION_EVENT,
- new StateChangeResult(networkId, BSSID, newState)));
- }
- }
-
- /**
- * Send the tracker a notification that the state of the supplicant
- * has changed.
- * @param networkId the configured network on which the state change occurred
- * @param newState the new {@code SupplicantState}
- */
- void notifySupplicantStateChange(int networkId, String BSSID, SupplicantState newState) {
- sendMessage(obtainMessage(SUPPLICANT_STATE_CHANGE_EVENT,
- new StateChangeResult(networkId, BSSID, newState)));
- }
-
- /**
- * Send the tracker a notification that a scan has completed, and results
- * are available.
- */
- void notifyScanResultsAvailable() {
- /**
- * Switch scan mode over to passive.
- * Turning off scan-only mode happens only in "Connect" mode
- */
- setScanType(false);
- sendMessage(SCAN_RESULTS_EVENT);
- }
-
- void notifyDriverStarted() {
- sendMessage(DRIVER_START_EVENT);
- }
-
- void notifyDriverStopped() {
- sendMessage(DRIVER_STOP_EVENT);
- }
-
- void notifyDriverHung() {
- setWifiEnabled(false);
- setWifiEnabled(true);
- }
-
-
- /********************************************************
- * HSM states
- *******************************************************/
-
- class DefaultState extends HierarchicalState {
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- SyncParams syncParams;
- switch (message.what) {
- /* Synchronous call returns */
- case CMD_PING_SUPPLICANT:
- case CMD_START_SCAN:
- case CMD_DISCONNECT:
- case CMD_RECONNECT:
- case CMD_REASSOCIATE:
- case CMD_REMOVE_NETWORK:
- case CMD_ENABLE_NETWORK:
- case CMD_DISABLE_NETWORK:
- case CMD_ADD_OR_UPDATE_NETWORK:
- case CMD_GET_RSSI:
- case CMD_GET_RSSI_APPROX:
- case CMD_GET_LINK_SPEED:
- case CMD_GET_MAC_ADDR:
- case CMD_SAVE_CONFIG:
- case CMD_CONNECTION_STATUS:
- case CMD_GET_NETWORK_CONFIG:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = false;
- syncParams.mSyncReturn.intValue = -1;
- syncParams.mSyncReturn.stringValue = null;
- syncParams.mSyncReturn.configList = null;
- notifyOnMsgObject(message);
- }
- break;
- case CM_CMD_TEARDOWN:
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- mTeardownRequested.set(true);
- sendMessage(CMD_DISCONNECT);
- sendMessage(CMD_STOP_DRIVER);
- /* Mark wifi available when CM tears down */
- mNetworkInfo.setIsAvailable(true);
- break;
- case CM_CMD_RECONNECT:
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- mTeardownRequested.set(false);
- sendMessage(CMD_START_DRIVER);
- sendMessage(CMD_RECONNECT);
- break;
- case CMD_ENABLE_RSSI_POLL:
- mEnableRssiPolling = (message.arg1 == 1);
- mSupplicantStateTracker.sendMessage(CMD_ENABLE_RSSI_POLL);
- break;
- default:
- if (DBG) Log.w(TAG, "Unhandled " + message);
- break;
- }
- return HANDLED;
- }
- }
-
- class InitialState extends HierarchicalState {
- @Override
- //TODO: could move logging into a common class
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- // [31-8] Reserved for future use
- // [7 - 0] HSM state change
- // 50021 wifi_state_changed (custom|1|5)
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-
- if (WifiNative.isDriverLoaded()) {
- transitionTo(mDriverLoadedState);
- }
- else {
- transitionTo(mDriverUnloadedState);
- }
- }
- }
-
- class DriverLoadingState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-
- final Message message = new Message();
- message.copyFrom(getCurrentMessage());
- new Thread(new Runnable() {
- public void run() {
- sWakeLock.acquire();
- //enabling state
- switch(message.arg1) {
- case WIFI_STATE_ENABLING:
- setWifiState(WIFI_STATE_ENABLING);
- break;
- case WIFI_AP_STATE_ENABLING:
- setWifiApState(WIFI_AP_STATE_ENABLING);
- break;
- }
-
- if(WifiNative.loadDriver()) {
- Log.d(TAG, "Driver load successful");
- sendMessage(CMD_LOAD_DRIVER_SUCCESS);
- } else {
- Log.e(TAG, "Failed to load driver!");
- switch(message.arg1) {
- case WIFI_STATE_ENABLING:
- setWifiState(WIFI_STATE_UNKNOWN);
- break;
- case WIFI_AP_STATE_ENABLING:
- setWifiApState(WIFI_AP_STATE_FAILED);
- break;
- }
- sendMessage(CMD_LOAD_DRIVER_FAILURE);
- }
- sWakeLock.release();
- }
- }).start();
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch (message.what) {
- case CMD_LOAD_DRIVER_SUCCESS:
- transitionTo(mDriverLoadedState);
- break;
- case CMD_LOAD_DRIVER_FAILURE:
- transitionTo(mDriverFailedState);
- break;
- case CMD_LOAD_DRIVER:
- case CMD_UNLOAD_DRIVER:
- case CMD_START_SUPPLICANT:
- case CMD_STOP_SUPPLICANT:
- case CMD_START_AP:
- case CMD_STOP_AP:
- case CMD_START_DRIVER:
- case CMD_STOP_DRIVER:
- case CMD_SET_SCAN_MODE:
- case CMD_SET_SCAN_TYPE:
- case CMD_SET_POWER_MODE:
- case CMD_SET_BLUETOOTH_COEXISTENCE:
- case CMD_SET_BLUETOOTH_SCAN_MODE:
- case CMD_SET_NUM_ALLOWED_CHANNELS:
- case CMD_START_PACKET_FILTERING:
- case CMD_STOP_PACKET_FILTERING:
- deferMessage(message);
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class DriverLoadedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch(message.what) {
- case CMD_UNLOAD_DRIVER:
- transitionTo(mDriverUnloadingState);
- break;
- case CMD_START_SUPPLICANT:
- if(WifiNative.startSupplicant()) {
- Log.d(TAG, "Supplicant start successful");
- mWifiMonitor.startMonitoring();
- setWifiState(WIFI_STATE_ENABLED);
- transitionTo(mWaitForSupState);
- } else {
- Log.e(TAG, "Failed to start supplicant!");
- sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
- }
- break;
- case CMD_START_AP:
- try {
- nwService.startAccessPoint((WifiConfiguration) message.obj,
- mInterfaceName,
- SOFTAP_IFACE);
- } catch(Exception e) {
- Log.e(TAG, "Exception in startAccessPoint()");
- sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0));
- break;
- }
- Log.d(TAG, "Soft AP start successful");
- setWifiApState(WIFI_AP_STATE_ENABLED);
- transitionTo(mSoftApStartedState);
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class DriverUnloadingState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-
- final Message message = new Message();
- message.copyFrom(getCurrentMessage());
- new Thread(new Runnable() {
- public void run() {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- sWakeLock.acquire();
- if(WifiNative.unloadDriver()) {
- Log.d(TAG, "Driver unload successful");
- sendMessage(CMD_UNLOAD_DRIVER_SUCCESS);
-
- switch(message.arg1) {
- case WIFI_STATE_DISABLED:
- case WIFI_STATE_UNKNOWN:
- setWifiState(message.arg1);
- break;
- case WIFI_AP_STATE_DISABLED:
- case WIFI_AP_STATE_FAILED:
- setWifiApState(message.arg1);
- break;
- }
- } else {
- Log.e(TAG, "Failed to unload driver!");
- sendMessage(CMD_UNLOAD_DRIVER_FAILURE);
-
- switch(message.arg1) {
- case WIFI_STATE_DISABLED:
- case WIFI_STATE_UNKNOWN:
- setWifiState(WIFI_STATE_UNKNOWN);
- break;
- case WIFI_AP_STATE_DISABLED:
- case WIFI_AP_STATE_FAILED:
- setWifiApState(WIFI_AP_STATE_FAILED);
- break;
- }
- }
- sWakeLock.release();
- }
- }).start();
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch (message.what) {
- case CMD_UNLOAD_DRIVER_SUCCESS:
- transitionTo(mDriverUnloadedState);
- break;
- case CMD_UNLOAD_DRIVER_FAILURE:
- transitionTo(mDriverFailedState);
- break;
- case CMD_LOAD_DRIVER:
- case CMD_UNLOAD_DRIVER:
- case CMD_START_SUPPLICANT:
- case CMD_STOP_SUPPLICANT:
- case CMD_START_AP:
- case CMD_STOP_AP:
- case CMD_START_DRIVER:
- case CMD_STOP_DRIVER:
- case CMD_SET_SCAN_MODE:
- case CMD_SET_SCAN_TYPE:
- case CMD_SET_POWER_MODE:
- case CMD_SET_BLUETOOTH_COEXISTENCE:
- case CMD_SET_BLUETOOTH_SCAN_MODE:
- case CMD_SET_NUM_ALLOWED_CHANNELS:
- case CMD_START_PACKET_FILTERING:
- case CMD_STOP_PACKET_FILTERING:
- deferMessage(message);
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class DriverUnloadedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch (message.what) {
- case CMD_LOAD_DRIVER:
- transitionTo(mDriverLoadingState);
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class DriverFailedState extends HierarchicalState {
- @Override
- public void enter() {
- Log.e(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- return NOT_HANDLED;
- }
- }
-
-
- class WaitForSupState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch(message.what) {
- case SUP_CONNECTION_EVENT:
- Log.d(TAG, "Supplicant connection established");
- mSupplicantStateTracker.resetSupplicantState();
- /* Initialize data structures */
- resetNotificationTimer();
- setTeardownRequested(false);
- mLastBssid = null;
- mLastNetworkId = -1;
- mLastSignalLevel = -1;
-
- mWifiInfo.setMacAddress(WifiNative.getMacAddressCommand());
-
- //TODO: initialize and fix multicast filtering
- //mWM.initializeMulticastFiltering();
-
- if (mBluetoothA2dp == null) {
- mBluetoothA2dp = new BluetoothA2dp(mContext);
- }
- checkIsBluetoothPlaying();
-
- checkUseStaticIp();
- sendSupplicantConnectionChangedBroadcast(true);
- transitionTo(mDriverSupReadyState);
- break;
- case CMD_STOP_SUPPLICANT:
- Log.d(TAG, "Stop supplicant received");
- WifiNative.stopSupplicant();
- transitionTo(mDriverLoadedState);
- break;
- /* Fail soft ap when waiting for supplicant start */
- case CMD_START_AP:
- Log.d(TAG, "Failed to start soft AP with a running supplicant");
- setWifiApState(WIFI_AP_STATE_FAILED);
- break;
- case CMD_START_DRIVER:
- case CMD_STOP_DRIVER:
- case CMD_SET_SCAN_MODE:
- case CMD_SET_SCAN_TYPE:
- case CMD_SET_POWER_MODE:
- case CMD_SET_BLUETOOTH_COEXISTENCE:
- case CMD_SET_BLUETOOTH_SCAN_MODE:
- case CMD_SET_NUM_ALLOWED_CHANNELS:
- case CMD_START_PACKET_FILTERING:
- case CMD_STOP_PACKET_FILTERING:
- deferMessage(message);
- break;
- case CMD_STOP_AP:
- case CMD_START_SUPPLICANT:
- case CMD_UNLOAD_DRIVER:
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class DriverSupReadyState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- /* Initialize for connect mode operation at start */
- mIsScanMode = false;
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- SyncParams syncParams;
- switch(message.what) {
- case CMD_STOP_SUPPLICANT: /* Supplicant stopped by user */
- Log.d(TAG, "Stop supplicant received");
- WifiNative.stopSupplicant();
- //$FALL-THROUGH$
- case SUP_DISCONNECTION_EVENT: /* Supplicant died */
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- //Remove any notifications on disconnection
- setNotificationVisible(false, 0, false, 0);
- WifiNative.closeSupplicantConnection();
- handleNetworkDisconnect();
- sendSupplicantConnectionChangedBroadcast(false);
- mSupplicantStateTracker.resetSupplicantState();
- transitionTo(mDriverLoadedState);
-
- /* When supplicant dies, unload driver and enter failed state */
- //TODO: consider bringing up supplicant again
- if (message.what == SUP_DISCONNECTION_EVENT) {
- Log.d(TAG, "Supplicant died, unloading driver");
- sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
- }
- break;
- case CMD_START_DRIVER:
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- WifiNative.startDriverCommand();
- transitionTo(mDriverStartingState);
- break;
- case SCAN_RESULTS_EVENT:
- setScanResults(WifiNative.scanResultsCommand());
- sendScanResultsAvailableBroadcast();
- checkAndSetNotification();
- break;
- case CMD_PING_SUPPLICANT:
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.pingCommand();
- notifyOnMsgObject(message);
- break;
- case CMD_ADD_OR_UPDATE_NETWORK:
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- syncParams = (SyncParams) message.obj;
- WifiConfiguration config = (WifiConfiguration) syncParams.mParameter;
- syncParams.mSyncReturn.intValue = addOrUpdateNetworkNative(config);
- notifyOnMsgObject(message);
- break;
- case CMD_REMOVE_NETWORK:
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.removeNetworkCommand(
- message.arg1);
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.removeNetworkCommand(message.arg1);
- }
- break;
- case CMD_ENABLE_NETWORK:
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- EnableNetParams enableNetParams = (EnableNetParams) syncParams.mParameter;
- syncParams.mSyncReturn.boolValue = WifiNative.enableNetworkCommand(
- enableNetParams.netId, enableNetParams.disableOthers);
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.enableNetworkCommand(message.arg1, message.arg2 == 1);
- }
- break;
- case CMD_DISABLE_NETWORK:
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.disableNetworkCommand(
- message.arg1);
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.disableNetworkCommand(message.arg1);
- }
- break;
- case CMD_BLACKLIST_NETWORK:
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- WifiNative.addToBlacklistCommand((String)message.obj);
- break;
- case CMD_CLEAR_BLACKLIST:
- WifiNative.clearBlacklistCommand();
- break;
- case CMD_GET_NETWORK_CONFIG:
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.configList = getConfiguredNetworksNative();
- notifyOnMsgObject(message);
- break;
- case CMD_SAVE_CONFIG:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.saveConfigCommand();
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.saveConfigCommand();
- }
- // Inform the backup manager about a data change
- IBackupManager ibm = IBackupManager.Stub.asInterface(
- ServiceManager.getService(Context.BACKUP_SERVICE));
- if (ibm != null) {
- try {
- ibm.dataChanged("com.android.providers.settings");
- } catch (Exception e) {
- // Try again later
- }
- }
- break;
- case CMD_CONNECTION_STATUS:
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.stringValue = WifiNative.statusCommand();
- notifyOnMsgObject(message);
- break;
- case CMD_GET_MAC_ADDR:
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.stringValue = WifiNative.getMacAddressCommand();
- notifyOnMsgObject(message);
- break;
- /* Cannot start soft AP while in client mode */
- case CMD_START_AP:
- Log.d(TAG, "Failed to start soft AP with a running supplicant");
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- setWifiApState(WIFI_AP_STATE_FAILED);
- break;
- case CMD_SET_SCAN_MODE:
- mIsScanMode = (message.arg1 == SCAN_ONLY_MODE);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- class DriverStartingState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch(message.what) {
- case DRIVER_START_EVENT:
- transitionTo(mDriverStartedState);
- break;
- /* Queue driver commands & connection events */
- case CMD_START_DRIVER:
- case CMD_STOP_DRIVER:
- case SUPPLICANT_STATE_CHANGE_EVENT:
- case NETWORK_CONNECTION_EVENT:
- case NETWORK_DISCONNECTION_EVENT:
- case PASSWORD_MAY_BE_INCORRECT_EVENT:
- case CMD_SET_SCAN_TYPE:
- case CMD_SET_POWER_MODE:
- case CMD_SET_BLUETOOTH_COEXISTENCE:
- case CMD_SET_BLUETOOTH_SCAN_MODE:
- case CMD_SET_NUM_ALLOWED_CHANNELS:
- case CMD_START_PACKET_FILTERING:
- case CMD_STOP_PACKET_FILTERING:
- deferMessage(message);
- break;
- /* Queue the asynchronous version of these commands */
- case CMD_START_SCAN:
- case CMD_DISCONNECT:
- case CMD_REASSOCIATE:
- case CMD_RECONNECT:
- if (message.arg2 != SYNCHRONOUS_CALL) {
- deferMessage(message);
- }
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class DriverStartedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-
- try {
- mBatteryStats.noteWifiRunning();
- } catch (RemoteException ignore) {}
-
- /* Initialize channel count */
- setNumAllowedChannels();
-
- if (mIsScanMode) {
- WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
- WifiNative.disconnectCommand();
- transitionTo(mScanModeState);
- } else {
- WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
- /* If supplicant has already connected, before we could finish establishing
- * the control channel connection, we miss all the supplicant events.
- * Disconnect and reconnect when driver has started to ensure we receive
- * all supplicant events.
- *
- * TODO: This is a bit unclean, ideally the supplicant should never
- * connect until told to do so by the framework
- */
- WifiNative.disconnectCommand();
- WifiNative.reconnectCommand();
- transitionTo(mConnectModeState);
- }
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- SyncParams syncParams;
- switch(message.what) {
- case CMD_SET_SCAN_TYPE:
- if (message.arg1 == SCAN_ACTIVE) {
- WifiNative.setScanModeCommand(true);
- } else {
- WifiNative.setScanModeCommand(false);
- }
- break;
- case CMD_SET_POWER_MODE:
- WifiNative.setPowerModeCommand(message.arg1);
- break;
- case CMD_SET_BLUETOOTH_COEXISTENCE:
- WifiNative.setBluetoothCoexistenceModeCommand(message.arg1);
- break;
- case CMD_SET_BLUETOOTH_SCAN_MODE:
- WifiNative.setBluetoothCoexistenceScanModeCommand(message.arg1 == 1);
- break;
- case CMD_SET_NUM_ALLOWED_CHANNELS:
- mNumAllowedChannels = message.arg1;
- WifiNative.setNumAllowedChannelsCommand(message.arg1);
- break;
- case CMD_START_DRIVER:
- /* Ignore another driver start */
- break;
- case CMD_STOP_DRIVER:
- WifiNative.stopDriverCommand();
- transitionTo(mDriverStoppingState);
- break;
- case CMD_REQUEST_CM_WAKELOCK:
- if (mCm == null) {
- mCm = (ConnectivityManager)mContext.getSystemService(
- Context.CONNECTIVITY_SERVICE);
- }
- mCm.requestNetworkTransitionWakelock(TAG);
- break;
- case CMD_START_PACKET_FILTERING:
- WifiNative.startPacketFiltering();
- break;
- case CMD_STOP_PACKET_FILTERING:
- WifiNative.stopPacketFiltering();
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- @Override
- public void exit() {
- if (DBG) Log.d(TAG, getName() + "\n");
- try {
- mBatteryStats.noteWifiStopped();
- } catch (RemoteException ignore) { }
- }
- }
-
- class DriverStoppingState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch(message.what) {
- case DRIVER_STOP_EVENT:
- transitionTo(mDriverStoppedState);
- break;
- /* Queue driver commands */
- case CMD_START_DRIVER:
- case CMD_STOP_DRIVER:
- case CMD_SET_SCAN_TYPE:
- case CMD_SET_POWER_MODE:
- case CMD_SET_BLUETOOTH_COEXISTENCE:
- case CMD_SET_BLUETOOTH_SCAN_MODE:
- case CMD_SET_NUM_ALLOWED_CHANNELS:
- case CMD_START_PACKET_FILTERING:
- case CMD_STOP_PACKET_FILTERING:
- deferMessage(message);
- break;
- /* Queue the asynchronous version of these commands */
- case CMD_START_SCAN:
- case CMD_DISCONNECT:
- case CMD_REASSOCIATE:
- case CMD_RECONNECT:
- if (message.arg2 != SYNCHRONOUS_CALL) {
- deferMessage(message);
- }
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class DriverStoppedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-
- // Take down any open network notifications on driver stop
- setNotificationVisible(false, 0, false, 0);
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- return NOT_HANDLED;
- }
- }
-
- class ScanModeState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- SyncParams syncParams;
- switch(message.what) {
- case CMD_SET_SCAN_MODE:
- if (message.arg1 == SCAN_ONLY_MODE) {
- /* Ignore */
- return HANDLED;
- } else {
- WifiNative.setScanResultHandlingCommand(message.arg1);
- WifiNative.reconnectCommand();
- mIsScanMode = false;
- transitionTo(mDisconnectedState);
- }
- break;
- case CMD_START_SCAN:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.scanCommand(
- message.arg1 == SCAN_ACTIVE);
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
- }
- break;
- /* Ignore */
- case CMD_DISCONNECT:
- case CMD_RECONNECT:
- case CMD_REASSOCIATE:
- case SUPPLICANT_STATE_CHANGE_EVENT:
- case NETWORK_CONNECTION_EVENT:
- case NETWORK_DISCONNECTION_EVENT:
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class ConnectModeState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- SyncParams syncParams;
- StateChangeResult stateChangeResult;
- switch(message.what) {
- case PASSWORD_MAY_BE_INCORRECT_EVENT:
- mPasswordKeyMayBeIncorrect = true;
- break;
- case SUPPLICANT_STATE_CHANGE_EVENT:
- stateChangeResult = (StateChangeResult) message.obj;
- mSupplicantStateTracker.handleEvent(stateChangeResult);
- break;
- case CMD_START_SCAN:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = true;
- notifyOnMsgObject(message);
- }
- /* We need to set scan type in completed state */
- Message newMsg = obtainMessage();
- newMsg.copyFrom(message);
- mSupplicantStateTracker.sendMessage(newMsg);
- break;
- /* Do a redundant disconnect without transition */
- case CMD_DISCONNECT:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand();
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.disconnectCommand();
- }
- break;
- case CMD_RECONNECT:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.reconnectCommand();
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.reconnectCommand();
- }
- break;
- case CMD_REASSOCIATE:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.reassociateCommand();
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.reassociateCommand();
- }
- break;
- case SCAN_RESULTS_EVENT:
- /* Set the scan setting back to "connect" mode */
- WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
- /* Handle scan results */
- return NOT_HANDLED;
- case NETWORK_CONNECTION_EVENT:
- Log.d(TAG,"Network connection established");
- stateChangeResult = (StateChangeResult) message.obj;
-
- /* Remove any notifications */
- setNotificationVisible(false, 0, false, 0);
- resetNotificationTimer();
-
- mWifiInfo.setBSSID(mLastBssid = stateChangeResult.BSSID);
- mWifiInfo.setNetworkId(stateChangeResult.networkId);
- mLastNetworkId = stateChangeResult.networkId;
-
- /* send event to CM & network change broadcast */
- setDetailedState(DetailedState.OBTAINING_IPADDR);
- sendNetworkStateChangeBroadcast(mLastBssid);
-
- transitionTo(mConnectingState);
- break;
- case NETWORK_DISCONNECTION_EVENT:
- Log.d(TAG,"Network connection lost");
- handleNetworkDisconnect();
- transitionTo(mDisconnectedState);
- break;
- case CMD_GET_RSSI:
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.intValue = WifiNative.getRssiCommand();
- notifyOnMsgObject(message);
- break;
- case CMD_GET_RSSI_APPROX:
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.intValue = WifiNative.getRssiApproxCommand();
- notifyOnMsgObject(message);
- break;
- case CMD_GET_LINK_SPEED:
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.intValue = WifiNative.getLinkSpeedCommand();
- notifyOnMsgObject(message);
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class ConnectingState extends HierarchicalState {
- boolean modifiedBluetoothCoexistenceMode;
- int powerMode;
- Thread mDhcpThread;
-
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-
- if (!mUseStaticIp) {
-
- mDhcpThread = null;
- modifiedBluetoothCoexistenceMode = false;
- powerMode = DRIVER_POWER_MODE_AUTO;
-
- if (shouldDisableCoexistenceMode()) {
- /*
- * There are problems setting the Wi-Fi driver's power
- * mode to active when bluetooth coexistence mode is
- * enabled or sense.
- * <p>
- * We set Wi-Fi to active mode when
- * obtaining an IP address because we've found
- * compatibility issues with some routers with low power
- * mode.
- * <p>
- * In order for this active power mode to properly be set,
- * we disable coexistence mode until we're done with
- * obtaining an IP address. One exception is if we
- * are currently connected to a headset, since disabling
- * coexistence would interrupt that connection.
- */
- modifiedBluetoothCoexistenceMode = true;
-
- // Disable the coexistence mode
- WifiNative.setBluetoothCoexistenceModeCommand(
- WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
- }
-
- powerMode = WifiNative.getPowerModeCommand();
- if (powerMode < 0) {
- // Handle the case where supplicant driver does not support
- // getPowerModeCommand.
- powerMode = DRIVER_POWER_MODE_AUTO;
- }
- if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
- WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_ACTIVE);
- }
-
- Log.d(TAG, "DHCP request started");
- mDhcpThread = new Thread(new Runnable() {
- public void run() {
- if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {
- Log.d(TAG, "DHCP request succeeded");
- sendMessage(CMD_IP_CONFIG_SUCCESS);
- } else {
- Log.d(TAG, "DHCP request failed: " +
- NetworkUtils.getDhcpError());
- sendMessage(CMD_IP_CONFIG_FAILURE);
- }
- }
- });
- mDhcpThread.start();
- } else {
- if (NetworkUtils.configureInterface(mInterfaceName, mDhcpInfo)) {
- Log.v(TAG, "Static IP configuration succeeded");
- sendMessage(CMD_IP_CONFIG_SUCCESS);
- } else {
- Log.v(TAG, "Static IP configuration failed");
- sendMessage(CMD_IP_CONFIG_FAILURE);
- }
- }
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
-
- switch(message.what) {
- case CMD_IP_CONFIG_SUCCESS:
- mReconnectCount = 0;
- mLastSignalLevel = -1; // force update of signal strength
- mWifiInfo.setIpAddress(mDhcpInfo.ipAddress);
- Log.d(TAG, "IP configuration: " + mDhcpInfo);
- configureNetworkProperties();
- setDetailedState(DetailedState.CONNECTED);
- sendNetworkStateChangeBroadcast(mLastBssid);
- transitionTo(mConnectedState);
- break;
- case CMD_IP_CONFIG_FAILURE:
- mWifiInfo.setIpAddress(0);
-
- Log.e(TAG, "IP configuration failed");
- /**
- * If we've exceeded the maximum number of retries for DHCP
- * to a given network, disable the network
- */
- if (++mReconnectCount > getMaxDhcpRetries()) {
- Log.e(TAG, "Failed " +
- mReconnectCount + " times, Disabling " + mLastNetworkId);
- WifiNative.disableNetworkCommand(mLastNetworkId);
- }
-
- /* DHCP times out after about 30 seconds, we do a
- * disconnect and an immediate reconnect to try again
- */
- WifiNative.disconnectCommand();
- WifiNative.reconnectCommand();
- transitionTo(mDisconnectingState);
- break;
- case CMD_DISCONNECT:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- SyncParams syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand();
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.disconnectCommand();
- }
- transitionTo(mDisconnectingState);
- break;
- /* Ignore */
- case NETWORK_CONNECTION_EVENT:
- break;
- case CMD_STOP_DRIVER:
- sendMessage(CMD_DISCONNECT);
- deferMessage(message);
- break;
- case CMD_SET_SCAN_MODE:
- if (message.arg1 == SCAN_ONLY_MODE) {
- sendMessage(CMD_DISCONNECT);
- deferMessage(message);
- }
- break;
- case CMD_RECONFIGURE_IP:
- deferMessage(message);
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
-
- @Override
- public void exit() {
- /* reset power state & bluetooth coexistence if on DHCP */
- if (!mUseStaticIp) {
- if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
- WifiNative.setPowerModeCommand(powerMode);
- }
-
- if (modifiedBluetoothCoexistenceMode) {
- // Set the coexistence mode back to its default value
- WifiNative.setBluetoothCoexistenceModeCommand(
- WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
- }
- }
-
- }
- }
-
- class ConnectedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch (message.what) {
- case CMD_DISCONNECT:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- SyncParams syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand();
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.disconnectCommand();
- }
- transitionTo(mDisconnectingState);
- break;
- case CMD_RECONFIGURE_IP:
- Log.d(TAG,"Reconfiguring IP on connection");
- NetworkUtils.resetConnections(mInterfaceName);
- transitionTo(mConnectingState);
- break;
- case CMD_STOP_DRIVER:
- sendMessage(CMD_DISCONNECT);
- deferMessage(message);
- break;
- case CMD_SET_SCAN_MODE:
- if (message.arg1 == SCAN_ONLY_MODE) {
- sendMessage(CMD_DISCONNECT);
- deferMessage(message);
- }
- break;
- /* Ignore */
- case NETWORK_CONNECTION_EVENT:
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class DisconnectingState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch (message.what) {
- case CMD_STOP_DRIVER: /* Stop driver only after disconnect handled */
- deferMessage(message);
- break;
- case CMD_SET_SCAN_MODE:
- if (message.arg1 == SCAN_ONLY_MODE) {
- deferMessage(message);
- }
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class DisconnectedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch (message.what) {
- case CMD_SET_SCAN_MODE:
- if (message.arg1 == SCAN_ONLY_MODE) {
- WifiNative.setScanResultHandlingCommand(message.arg1);
- //Supplicant disconnect to prevent further connects
- WifiNative.disconnectCommand();
- mIsScanMode = true;
- transitionTo(mScanModeState);
- }
- break;
- /* Ignore network disconnect */
- case NETWORK_DISCONNECTION_EVENT:
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class SoftApStartedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch(message.what) {
- case CMD_STOP_AP:
- Log.d(TAG,"Stopping Soft AP");
- setWifiApState(WIFI_AP_STATE_DISABLING);
- try {
- nwService.stopAccessPoint();
- } catch(Exception e) {
- Log.e(TAG, "Exception in stopAccessPoint()");
- }
- transitionTo(mDriverLoadedState);
- break;
- case CMD_START_AP:
- Log.d(TAG,"SoftAP set on a running access point");
- try {
- nwService.setAccessPoint((WifiConfiguration) message.obj,
- mInterfaceName,
- SOFTAP_IFACE);
- } catch(Exception e) {
- Log.e(TAG, "Exception in nwService during soft AP set");
- try {
- nwService.stopAccessPoint();
- } catch (Exception ee) {
- Slog.e(TAG, "Could not stop AP, :" + ee);
- }
- sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0));
- }
- break;
- /* Fail client mode operation when soft AP is enabled */
- case CMD_START_SUPPLICANT:
- Log.e(TAG,"Cannot start supplicant with a running soft AP");
- setWifiState(WIFI_STATE_UNKNOWN);
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
-
- class SupplicantStateTracker extends HierarchicalStateMachine {
-
- private int mRssiPollToken = 0;
-
- /**
- * The max number of the WPA supplicant loop iterations before we
- * decide that the loop should be terminated:
- */
- private static final int MAX_SUPPLICANT_LOOP_ITERATIONS = 4;
- private int mLoopDetectIndex = 0;
- private int mLoopDetectCount = 0;
-
- /**
- * Supplicant state change commands follow
- * the ordinal values defined in SupplicantState.java
- */
- private static final int DISCONNECTED = 0;
- private static final int INACTIVE = 1;
- private static final int SCANNING = 2;
- private static final int ASSOCIATING = 3;
- private static final int ASSOCIATED = 4;
- private static final int FOUR_WAY_HANDSHAKE = 5;
- private static final int GROUP_HANDSHAKE = 6;
- private static final int COMPLETED = 7;
- private static final int DORMANT = 8;
- private static final int UNINITIALIZED = 9;
- private static final int INVALID = 10;
-
- private HierarchicalState mUninitializedState;
- private HierarchicalState mInitializedState;
- private HierarchicalState mInactiveState;
- private HierarchicalState mDisconnectState;
- private HierarchicalState mScanState;
- private HierarchicalState mConnectState;
- private HierarchicalState mHandshakeState;
- private HierarchicalState mCompletedState;
- private HierarchicalState mDormantState;
-
-
- public SupplicantStateTracker(Context context, Handler target) {
- super(TAG, target.getLooper());
-
- mUninitializedState = new UninitializedState();
- mInitializedState = new InitializedState();
- mInactiveState = new InactiveState();
- mDisconnectState = new DisconnectedState();
- mScanState = new ScanState();
- mConnectState = new ConnectState();
- mHandshakeState = new HandshakeState();
- mCompletedState = new CompletedState();
- mDormantState = new DormantState();
-
-
- addState(mUninitializedState);
- addState(mInitializedState);
- addState(mInactiveState, mInitializedState);
- addState(mDisconnectState, mInitializedState);
- addState(mScanState, mInitializedState);
- addState(mConnectState, mInitializedState);
- addState(mHandshakeState, mConnectState);
- addState(mCompletedState, mConnectState);
- addState(mDormantState, mInitializedState);
-
- setInitialState(mUninitializedState);
-
- //start the state machine
- start();
- }
-
- public void handleEvent(StateChangeResult stateChangeResult) {
- SupplicantState newState = (SupplicantState) stateChangeResult.state;
-
- // Supplicant state change
- // [31-13] Reserved for future use
- // [8 - 0] Supplicant state (as defined in SupplicantState.java)
- // 50023 supplicant_state_changed (custom|1|5)
- EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, newState.ordinal());
-
- sendMessage(obtainMessage(newState.ordinal(), stateChangeResult));
- }
-
- public void resetSupplicantState() {
- transitionTo(mUninitializedState);
- }
-
- private void resetLoopDetection() {
- mLoopDetectCount = 0;
- mLoopDetectIndex = 0;
- }
-
- private boolean handleTransition(Message msg) {
- if (DBG) Log.d(TAG, getName() + msg.toString() + "\n");
- switch (msg.what) {
- case DISCONNECTED:
- transitionTo(mDisconnectState);
- break;
- case SCANNING:
- transitionTo(mScanState);
- break;
- case ASSOCIATING:
- StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
- /* BSSID is valid only in ASSOCIATING state */
- mWifiInfo.setBSSID(stateChangeResult.BSSID);
- //$FALL-THROUGH$
- case ASSOCIATED:
- case FOUR_WAY_HANDSHAKE:
- case GROUP_HANDSHAKE:
- transitionTo(mHandshakeState);
- break;
- case COMPLETED:
- transitionTo(mCompletedState);
- break;
- case DORMANT:
- transitionTo(mDormantState);
- break;
- case INACTIVE:
- transitionTo(mInactiveState);
- break;
- case UNINITIALIZED:
- case INVALID:
- transitionTo(mUninitializedState);
- break;
- default:
- return NOT_HANDLED;
- }
- StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
- SupplicantState supState = (SupplicantState) stateChangeResult.state;
- setDetailedState(WifiInfo.getDetailedStateOf(supState));
- mWifiInfo.setSupplicantState(supState);
- mWifiInfo.setNetworkId(stateChangeResult.networkId);
- //TODO: Modify WifiMonitor to report SSID on events
- //mWifiInfo.setSSID()
- return HANDLED;
- }
-
- /********************************************************
- * HSM states
- *******************************************************/
-
- class InitializedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch (message.what) {
- case CMD_START_SCAN:
- WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
- break;
- default:
- if (DBG) Log.w(TAG, "Ignoring " + message);
- break;
- }
- return HANDLED;
- }
- }
-
- class UninitializedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- mNetworkInfo.setIsAvailable(false);
- resetLoopDetection();
- mPasswordKeyMayBeIncorrect = false;
- }
- @Override
- public boolean processMessage(Message message) {
- switch(message.what) {
- default:
- if (!handleTransition(message)) {
- if (DBG) Log.w(TAG, "Ignoring " + message);
- }
- break;
- }
- return HANDLED;
- }
- }
-
- class InactiveState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- Message message = getCurrentMessage();
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
-
- mNetworkInfo.setIsAvailable(false);
- resetLoopDetection();
- mPasswordKeyMayBeIncorrect = false;
-
- sendSupplicantStateChangedBroadcast(stateChangeResult, false);
- }
- @Override
- public boolean processMessage(Message message) {
- return handleTransition(message);
- }
- }
-
-
- class DisconnectedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- Message message = getCurrentMessage();
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
-
- mNetworkInfo.setIsAvailable(true);
- resetLoopDetection();
-
- /* If a disconnect event happens after a password key failure
- * event, disable the network
- */
- if (mPasswordKeyMayBeIncorrect) {
- Log.d(TAG, "Failed to authenticate, disabling network " +
- mWifiInfo.getNetworkId());
- WifiNative.disableNetworkCommand(mWifiInfo.getNetworkId());
- mPasswordKeyMayBeIncorrect = false;
- sendSupplicantStateChangedBroadcast(stateChangeResult, true);
- }
- else {
- sendSupplicantStateChangedBroadcast(stateChangeResult, false);
- }
- }
- @Override
- public boolean processMessage(Message message) {
- return handleTransition(message);
- }
- }
-
- class ScanState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- Message message = getCurrentMessage();
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
-
- mNetworkInfo.setIsAvailable(true);
- mPasswordKeyMayBeIncorrect = false;
- resetLoopDetection();
- sendSupplicantStateChangedBroadcast(stateChangeResult, false);
- }
- @Override
- public boolean processMessage(Message message) {
- return handleTransition(message);
- }
- }
-
- class ConnectState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- }
- @Override
- public boolean processMessage(Message message) {
- switch (message.what) {
- case CMD_START_SCAN:
- WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
- WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- class HandshakeState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- final Message message = getCurrentMessage();
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
-
- mNetworkInfo.setIsAvailable(true);
-
- if (mLoopDetectIndex > message.what) {
- mLoopDetectCount++;
- }
- if (mLoopDetectCount > MAX_SUPPLICANT_LOOP_ITERATIONS) {
- WifiNative.disableNetworkCommand(stateChangeResult.networkId);
- mLoopDetectCount = 0;
- }
-
- mLoopDetectIndex = message.what;
-
- mPasswordKeyMayBeIncorrect = false;
- sendSupplicantStateChangedBroadcast(stateChangeResult, false);
- }
- @Override
- public boolean processMessage(Message message) {
- return handleTransition(message);
- }
- }
-
- class CompletedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- Message message = getCurrentMessage();
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
-
- mNetworkInfo.setIsAvailable(true);
-
- mRssiPollToken++;
- if (mEnableRssiPolling) {
- sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
- POLL_RSSI_INTERVAL_MSECS);
- }
-
- resetLoopDetection();
-
- mPasswordKeyMayBeIncorrect = false;
- sendSupplicantStateChangedBroadcast(stateChangeResult, false);
- }
- @Override
- public boolean processMessage(Message message) {
- switch(message.what) {
- case ASSOCIATING:
- case ASSOCIATED:
- case FOUR_WAY_HANDSHAKE:
- case GROUP_HANDSHAKE:
- case COMPLETED:
- break;
- case CMD_RSSI_POLL:
- if (message.arg1 == mRssiPollToken) {
- // Get Info and continue polling
- requestPolledInfo();
- sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
- POLL_RSSI_INTERVAL_MSECS);
- } else {
- // Polling has completed
- }
- break;
- case CMD_ENABLE_RSSI_POLL:
- mRssiPollToken++;
- if (mEnableRssiPolling) {
- // first poll
- requestPolledInfo();
- sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
- POLL_RSSI_INTERVAL_MSECS);
- }
- break;
- default:
- return handleTransition(message);
- }
- return HANDLED;
- }
- }
-
- class DormantState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- Message message = getCurrentMessage();
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
-
- mNetworkInfo.setIsAvailable(true);
- resetLoopDetection();
- mPasswordKeyMayBeIncorrect = false;
-
- sendSupplicantStateChangedBroadcast(stateChangeResult, false);
-
- /* TODO: reconnect is now being handled at DHCP failure handling
- * If we run into issues with staying in Dormant state, might
- * need a reconnect here
- */
- }
- @Override
- public boolean processMessage(Message message) {
- return handleTransition(message);
- }
- }
- }
-
- private class NotificationEnabledSettingObserver extends ContentObserver {
-
- public NotificationEnabledSettingObserver(Handler handler) {
- super(handler);
- }
-
- public void register() {
- ContentResolver cr = mContext.getContentResolver();
- cr.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
- mNotificationEnabled = getValue();
- }
-
- @Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
-
- mNotificationEnabled = getValue();
- if (!mNotificationEnabled) {
- // Remove any notification that may be showing
- setNotificationVisible(false, 0, true, 0);
- }
-
- resetNotificationTimer();
- }
-
- private boolean getValue() {
- return Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
- }
- }
-}
+}
\ No newline at end of file