Merge "clarify dp dimension description" into froyo
diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java
index 78bbb4f..009671f 100644
--- a/core/java/android/app/DatePickerDialog.java
+++ b/core/java/android/app/DatePickerDialog.java
@@ -35,6 +35,9 @@
/**
* A simple dialog containing an {@link android.widget.DatePicker}.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-datepicker.html">Date Picker
+ * tutorial</a>.</p>
*/
public class DatePickerDialog extends AlertDialog implements OnClickListener,
OnDateChangedListener {
diff --git a/core/java/android/app/TimePickerDialog.java b/core/java/android/app/TimePickerDialog.java
index 002b01f..62d3f7d 100644
--- a/core/java/android/app/TimePickerDialog.java
+++ b/core/java/android/app/TimePickerDialog.java
@@ -32,6 +32,9 @@
/**
* A dialog that prompts the user for the time of day using a {@link TimePicker}.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-timepicker.html">Time Picker
+ * tutorial</a>.</p>
*/
public class TimePickerDialog extends AlertDialog implements OnClickListener,
OnTimeChangedListener {
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 2df250d..bbd8b95 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -117,6 +117,9 @@
* href="{@docRoot}guide/topics/manifest/manifest-element.html">{@code <manifest>}</a>
* element.</p>
*
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-webview.html">Web View
+ * tutorial</a>.</p>
+ *
* <h3>Basic usage</h3>
*
* <p>By default, a WebView provides no browser-like widgets, does not
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index e15a520..3971487 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -76,6 +76,9 @@
* }
* </pre>
*
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-autocomplete.html">Auto Complete
+ * tutorial</a>.</p>
+ *
* @attr ref android.R.styleable#AutoCompleteTextView_completionHint
* @attr ref android.R.styleable#AutoCompleteTextView_completionThreshold
* @attr ref android.R.styleable#AutoCompleteTextView_completionHintView
diff --git a/core/java/android/widget/Button.java b/core/java/android/widget/Button.java
index 5e692d4..176233e 100644
--- a/core/java/android/widget/Button.java
+++ b/core/java/android/widget/Button.java
@@ -48,6 +48,9 @@
* }
* </pre>
*
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
+ *
* <p><strong>XML attributes</strong></p>
* <p>
* See {@link android.R.styleable#Button Button Attributes},
diff --git a/core/java/android/widget/CheckBox.java b/core/java/android/widget/CheckBox.java
index ff63a24..b89c2a9 100644
--- a/core/java/android/widget/CheckBox.java
+++ b/core/java/android/widget/CheckBox.java
@@ -41,6 +41,9 @@
* }
* }
* </pre>
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
*
* <p><strong>XML attributes</strong></p>
* <p>
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index b3d5f1a..a4ee68a 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -37,6 +37,9 @@
/**
* A view for selecting a month / year / day based on a calendar like layout.
*
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-datepicker.html">Date Picker
+ * tutorial</a>.</p>
+ *
* For a dialog using this view, see {@link android.app.DatePickerDialog}.
*/
@Widget
diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java
index 1532db1..0da68a4 100644
--- a/core/java/android/widget/EditText.java
+++ b/core/java/android/widget/EditText.java
@@ -35,6 +35,9 @@
/**
* EditText is a thin veneer over TextView that configures itself
* to be editable.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
* <p>
* <b>XML attributes</b>
* <p>
diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java
index f34823c..aea8042 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -46,6 +46,9 @@
* <p>
* Views given to the Gallery should use {@link Gallery.LayoutParams} as their
* layout parameters type.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-gallery.html">Gallery
+ * tutorial</a>.</p>
*
* @attr ref android.R.styleable#Gallery_animationDuration
* @attr ref android.R.styleable#Gallery_spacing
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index d2829db..2f86d75 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -31,6 +31,9 @@
/**
* A view that shows items in two-dimensional scrolling grid. The items in the
* grid come from the {@link ListAdapter} associated with this view.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-gridview.html">Grid
+ * View tutorial</a>.</p>
*/
public class GridView extends AbsListView {
public static final int NO_STRETCH = 0;
diff --git a/core/java/android/widget/ImageButton.java b/core/java/android/widget/ImageButton.java
index 5c05170..12a68db 100644
--- a/core/java/android/widget/ImageButton.java
+++ b/core/java/android/widget/ImageButton.java
@@ -62,6 +62,9 @@
* it will only be applied after {@code android:state_pressed} and {@code
* android:state_focused} have both evaluated false.</p>
*
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
+ *
* <p><strong>XML attributes</strong></p>
* <p>
* See {@link android.R.styleable#ImageView Button Attributes},
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index bd07e1f..3d7940c 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -37,6 +37,9 @@
* {@link android.widget.LinearLayout.LayoutParams LinearLayout.LayoutParams}.
* The default orientation is horizontal.
*
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-linearlayout.html">Linear Layout
+ * tutorial</a>.</p>
+ *
* <p>
* Also see {@link LinearLayout.LayoutParams android.widget.LinearLayout.LayoutParams}
* for layout attributes </p>
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 892c44a..3015406 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -58,6 +58,9 @@
* A view that shows items in a vertically scrolling list. The items
* come from the {@link ListAdapter} associated with this view.
*
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-listview.html">List View
+ * tutorial</a>.</p>
+ *
* @attr ref android.R.styleable#ListView_entries
* @attr ref android.R.styleable#ListView_divider
* @attr ref android.R.styleable#ListView_dividerHeight
diff --git a/core/java/android/widget/RadioButton.java b/core/java/android/widget/RadioButton.java
index 14ec8c6..ebbe1cd 100644
--- a/core/java/android/widget/RadioButton.java
+++ b/core/java/android/widget/RadioButton.java
@@ -34,6 +34,9 @@
* a radio group, checking one radio button unchecks all the others.</p>
* </p>
*
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
+ *
* <p><strong>XML attributes</strong></p>
* <p>
* See {@link android.R.styleable#CompoundButton CompoundButton Attributes},
diff --git a/core/java/android/widget/RatingBar.java b/core/java/android/widget/RatingBar.java
index 1800c5a..28499d0 100644
--- a/core/java/android/widget/RatingBar.java
+++ b/core/java/android/widget/RatingBar.java
@@ -41,6 +41,9 @@
* <p>
* The secondary progress should not be modified by the client as it is used
* internally as the background for a fractionally filled star.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
*
* @attr ref android.R.styleable#RatingBar_numStars
* @attr ref android.R.styleable#RatingBar_rating
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 1aa1df3..1630e7f 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -54,6 +54,9 @@
* {@link #ALIGN_PARENT_BOTTOM}.
* </p>
*
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-relativelayout.html">Relative
+ * Layout tutorial</a>.</p>
+ *
* <p>
* Also see {@link android.widget.RelativeLayout.LayoutParams RelativeLayout.LayoutParams} for
* layout attributes
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index 2f6dd1e..8ddb06c 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -32,6 +32,9 @@
* A view that displays one child at a time and lets the user pick among them.
* The items in the Spinner come from the {@link Adapter} associated with
* this view.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-spinner.html">Spinner
+ * tutorial</a>.</p>
*
* @attr ref android.R.styleable#Spinner_prompt
*/
diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java
index 02cd6a8..f720cee 100644
--- a/core/java/android/widget/TabHost.java
+++ b/core/java/android/widget/TabHost.java
@@ -40,6 +40,9 @@
* user clicks to select a specific tab, and a FrameLayout object that displays the contents of that
* page. The individual elements are typically controlled using this container object, rather than
* setting values on the child elements themselves.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-tabwidget.html">Tab Layout
+ * tutorial</a>.</p>
*/
public class TabHost extends FrameLayout implements ViewTreeObserver.OnTouchModeChangeListener {
diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java
index 4e1b585..afae7ef 100644
--- a/core/java/android/widget/TabWidget.java
+++ b/core/java/android/widget/TabWidget.java
@@ -40,6 +40,9 @@
* handler, and manage callbacks. You might call this object to iterate the list
* of tabs, or to tweak the layout of the tab list, but most methods should be
* called on the containing TabHost object.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-tabwidget.html">Tab Layout
+ * tutorial</a>.</p>
*
* @attr ref android.R.styleable#TabWidget_divider
* @attr ref android.R.styleable#TabWidget_tabStripEnabled
diff --git a/core/java/android/widget/TableLayout.java b/core/java/android/widget/TableLayout.java
index 73760ac..7f26e28 100644
--- a/core/java/android/widget/TableLayout.java
+++ b/core/java/android/widget/TableLayout.java
@@ -67,6 +67,9 @@
* <p>Although the typical child of a TableLayout is a TableRow, you can
* actually use any View subclass as a direct child of TableLayout. The View
* will be displayed as a single row that spans all the table columns.</p>
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-tablelayout.html">Table
+ * Layout tutorial</a>.</p>
*/
public class TableLayout extends LinearLayout {
private int[] mMaxWidths;
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index caed308..e61fac3 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -45,6 +45,9 @@
* Under AM/PM mode, the user can hit 'a', 'A", 'p' or 'P' to pick.
*
* For a dialog using this view, see {@link android.app.TimePickerDialog}.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-timepicker.html">Time Picker
+ * tutorial</a>.</p>
*/
@Widget
public class TimePicker extends FrameLayout {
diff --git a/core/java/android/widget/ToggleButton.java b/core/java/android/widget/ToggleButton.java
index dc791e3..3b680e8 100644
--- a/core/java/android/widget/ToggleButton.java
+++ b/core/java/android/widget/ToggleButton.java
@@ -26,6 +26,9 @@
/**
* Displays checked/unchecked states as a button
* with a "light" indicator and by default accompanied with the text "ON" or "OFF".
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Form Stuff
+ * tutorial</a>.</p>
*
* @attr ref android.R.styleable#ToggleButton_textOn
* @attr ref android.R.styleable#ToggleButton_textOff
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 036da95..ce73ae1 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1205,8 +1205,12 @@
</application>
- <instrumentation
- android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="com.android.frameworks.coretests"
- android:label="Frameworks Core Tests" />
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="com.android.frameworks.coretests"
+ android:label="Frameworks Core Tests" />
+
+ <instrumentation android:name="android.bluetooth.BluetoothTestRunner"
+ android:targetPackage="com.android.frameworks.coretests"
+ android:label="Bluetooth Tests" />
+
</manifest>
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothRebootStressTest.java b/core/tests/coretests/src/android/bluetooth/BluetoothRebootStressTest.java
new file mode 100644
index 0000000..33e9dd7
--- /dev/null
+++ b/core/tests/coretests/src/android/bluetooth/BluetoothRebootStressTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.bluetooth;
+
+import android.content.Context;
+import android.test.InstrumentationTestCase;
+
+/**
+ * Instrumentation test case for stress test involving rebooting the device.
+ * <p>
+ * This test case tests that bluetooth is enabled after a device reboot. Because
+ * the device will reboot, the instrumentation must be driven by a script on the
+ * host side.
+ */
+public class BluetoothRebootStressTest extends InstrumentationTestCase {
+ private static final String TAG = "BluetoothRebootStressTest";
+ private static final String OUTPUT_FILE = "BluetoothRebootStressTestOutput.txt";
+
+ private BluetoothTestUtils mTestUtils;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ Context context = getInstrumentation().getTargetContext();
+ mTestUtils = new BluetoothTestUtils(context, TAG, OUTPUT_FILE);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+
+ mTestUtils.close();
+ }
+
+ /**
+ * Test method used to start the test by turning bluetooth on.
+ */
+ public void testStart() {
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ mTestUtils.enable(adapter);
+ }
+
+ /**
+ * Test method used in the middle iterations of the test to check if
+ * bluetooth is on. Does not toggle bluetooth after the check. Assumes that
+ * bluetooth has been turned on by {@code #testStart()}
+ */
+ public void testMiddleNoToggle() {
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+
+ assertTrue(adapter.isEnabled());
+ }
+
+ /**
+ * Test method used in the middle iterations of the test to check if
+ * bluetooth is on. Toggles bluetooth after the check. Assumes that
+ * bluetooth has been turned on by {@code #testStart()}
+ */
+ public void testMiddleToggle() {
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+
+ assertTrue(adapter.isEnabled());
+
+ mTestUtils.disable(adapter);
+ mTestUtils.enable(adapter);
+ }
+
+ /**
+ * Test method used in the stop the test by turning bluetooth off. Assumes
+ * that bluetooth has been turned on by {@code #testStart()}
+ */
+ public void testStop() {
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+
+ assertTrue(adapter.isEnabled());
+
+ mTestUtils.disable(adapter);
+ }
+}
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
index cbd8714..d8d9eba 100644
--- a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
+++ b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
@@ -16,420 +16,66 @@
package android.bluetooth;
-import android.app.Instrumentation;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.test.InstrumentationTestCase;
-import android.util.Log;
public class BluetoothStressTest extends InstrumentationTestCase {
- private static final String TAG = "BluetoothEnablerStressTest";
+ private static final String TAG = "BluetoothStressTest";
+ private static final String OUTPUT_FILE = "BluetoothStressTestOutput.txt";
- /**
- * Timeout for {@link BluetoothAdapter#disable()} in ms.
- */
- private static final int DISABLE_TIMEOUT = 5000;
-
- /**
- * Timeout for {@link BluetoothAdapter#enable()} in ms.
- */
- private static final int ENABLE_TIMEOUT = 20000;
-
- /**
- * Timeout for {@link BluetoothAdapter#setScanMode(int)} in ms.
- */
- private static final int SET_SCAN_MODE_TIMEOUT = 5000;
-
- /**
- * Timeout for {@link BluetoothAdapter#startDiscovery()} in ms.
- */
- private static final int START_DISCOVERY_TIMEOUT = 5000;
-
- /**
- * Timeout for {@link BluetoothAdapter#cancelDiscovery()} in ms.
- */
- private static final int CANCEL_DISCOVERY_TIMEOUT = 5000;
-
- private static final int DISCOVERY_STARTED_FLAG = 1;
- private static final int DISCOVERY_FINISHED_FLAG = 1 << 1;
- private static final int SCAN_MODE_NONE_FLAG = 1 << 2;
- private static final int SCAN_MODE_CONNECTABLE_FLAG = 1 << 3;
- private static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG = 1 << 4;
- private static final int STATE_OFF_FLAG = 1 << 5;
- private static final int STATE_TURNING_ON_FLAG = 1 << 6;
- private static final int STATE_ON_FLAG = 1 << 7;
- private static final int STATE_TURNING_OFF_FLAG = 1 << 8;
-
- /**
- * Time between polls in ms.
- */
- private static final int POLL_TIME = 100;
-
- private static final int ENABLE_ITERATIONS = 100;
- private static final int DISCOVERABLE_ITERATIONS = 1000;
- private static final int SCAN_ITERATIONS = 1000;
-
- private Context mContext;
-
- private Instrumentation mInstrumentation;
-
- private class BluetoothReceiver extends BroadcastReceiver {
- private int mFiredFlags = 0;
-
- @Override
- public void onReceive(Context context, Intent intent) {
- synchronized (this) {
- if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(intent.getAction())) {
- mFiredFlags |= DISCOVERY_STARTED_FLAG;
- } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(intent.getAction())) {
- mFiredFlags |= DISCOVERY_FINISHED_FLAG;
- } else if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(intent.getAction())) {
- int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE,
- BluetoothAdapter.ERROR);
- assertNotSame(mode, BluetoothAdapter.ERROR);
- switch (mode) {
- case BluetoothAdapter.SCAN_MODE_NONE:
- mFiredFlags |= SCAN_MODE_NONE_FLAG;
- break;
- case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
- mFiredFlags |= SCAN_MODE_CONNECTABLE_FLAG;
- break;
- case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
- mFiredFlags |= SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG;
- break;
- }
- } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
- int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
- BluetoothAdapter.ERROR);
- assertNotSame(state, BluetoothAdapter.ERROR);
- switch (state) {
- case BluetoothAdapter.STATE_OFF:
- mFiredFlags |= STATE_OFF_FLAG;
- break;
- case BluetoothAdapter.STATE_TURNING_ON:
- mFiredFlags |= STATE_TURNING_ON_FLAG;
- break;
- case BluetoothAdapter.STATE_ON:
- mFiredFlags |= STATE_ON_FLAG;
- break;
- case BluetoothAdapter.STATE_TURNING_OFF:
- mFiredFlags |= STATE_TURNING_OFF_FLAG;
- break;
- }
- }
- }
- }
-
- public int getFiredFlags() {
- synchronized (this) {
- return mFiredFlags;
- }
- }
-
- public void resetFiredFlags() {
- synchronized (this) {
- mFiredFlags = 0;
- }
- }
- }
-
- private BluetoothReceiver mReceiver = new BluetoothReceiver();
+ private BluetoothTestUtils mTestUtils;
@Override
protected void setUp() throws Exception {
super.setUp();
- mInstrumentation = getInstrumentation();
- mContext = mInstrumentation.getTargetContext();
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
- filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
- filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
- filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
- mContext.registerReceiver(mReceiver, filter);
+ Context context = getInstrumentation().getTargetContext();
+ mTestUtils = new BluetoothTestUtils(context, TAG, OUTPUT_FILE);
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
- mContext.unregisterReceiver(mReceiver);
+ mTestUtils.close();
}
- public void testEnableDisable() {
+ public void testEnable() {
+ int iterations = BluetoothTestRunner.sEnableIterations;
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- for (int i = 0; i < ENABLE_ITERATIONS; i++) {
- Log.i(TAG, "Enable iteration " + (i + 1) + " of " + ENABLE_ITERATIONS);
- enable(adapter);
- disable(adapter);
+ for (int i = 0; i < iterations; i++) {
+ mTestUtils.writeOutput("enable iteration " + (i + 1) + " of " + iterations);
+ mTestUtils.enable(adapter);
+ mTestUtils.disable(adapter);
}
}
public void testDiscoverable() {
+ int iterations = BluetoothTestRunner.sDiscoverableIterations;
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- enable(adapter);
+ mTestUtils.enable(adapter);
- for (int i = 0; i < DISCOVERABLE_ITERATIONS; i++) {
- Log.i(TAG, "Discoverable iteration " + (i + 1) + " of " + DISCOVERABLE_ITERATIONS);
- discoverable(adapter);
- undiscoverable(adapter);
+ for (int i = 0; i < iterations; i++) {
+ mTestUtils.writeOutput("discoverable iteration " + (i + 1) + " of " + iterations);
+ mTestUtils.discoverable(adapter);
+ mTestUtils.undiscoverable(adapter);
}
- disable(adapter);
+ mTestUtils.disable(adapter);
}
public void testScan() {
+ int iterations = BluetoothTestRunner.sScanIterations;
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- enable(adapter);
+ mTestUtils.enable(adapter);
- for (int i = 0; i < SCAN_ITERATIONS; i++) {
- Log.i(TAG, "Scan iteration " + (i + 1) + " of " + SCAN_ITERATIONS);
- startScan(adapter);
- stopScan(adapter);
+ for (int i = 0; i < iterations; i++) {
+ mTestUtils.writeOutput("scan iteration " + (i + 1) + " of " + iterations);
+ mTestUtils.startScan(adapter);
+ mTestUtils.stopScan(adapter);
}
- disable(adapter);
- }
-
- private void disable(BluetoothAdapter adapter) {
- int mask = STATE_TURNING_OFF_FLAG | STATE_OFF_FLAG | SCAN_MODE_NONE_FLAG;
- mReceiver.resetFiredFlags();
-
- int state = adapter.getState();
- switch (state) {
- case BluetoothAdapter.STATE_OFF:
- assertFalse(adapter.isEnabled());
- return;
- case BluetoothAdapter.STATE_ON:
- assertTrue(adapter.isEnabled());
- assertTrue(adapter.disable());
- break;
- case BluetoothAdapter.STATE_TURNING_ON:
- assertFalse(adapter.isEnabled());
- assertTrue(adapter.disable());
- break;
- case BluetoothAdapter.STATE_TURNING_OFF:
- assertFalse(adapter.isEnabled());
- mask = 0; // Don't check for received intents since we might have missed them.
- break;
- default:
- fail("disable() invalid state: " + state);
- }
-
- long s = System.currentTimeMillis();
- while (System.currentTimeMillis() - s < DISABLE_TIMEOUT) {
- state = adapter.getState();
- if (state == BluetoothAdapter.STATE_OFF) {
- assertFalse(adapter.isEnabled());
- if ((mReceiver.getFiredFlags() & mask) == mask) {
- mReceiver.resetFiredFlags();
- return;
- }
- } else {
- assertFalse(adapter.isEnabled());
- assertEquals(BluetoothAdapter.STATE_TURNING_OFF, state);
- }
- sleep(POLL_TIME);
- }
-
- int firedFlags = mReceiver.getFiredFlags();
- mReceiver.resetFiredFlags();
- fail("disable() timeout: " +
- "state=" + state + " (expected " + BluetoothAdapter.STATE_OFF + ") " +
- "flags=" + firedFlags + " (expected " + mask + ")");
- }
-
- private void enable(BluetoothAdapter adapter) {
- int mask = STATE_TURNING_ON_FLAG | STATE_ON_FLAG | SCAN_MODE_CONNECTABLE_FLAG;
- mReceiver.resetFiredFlags();
-
- int state = adapter.getState();
- switch (state) {
- case BluetoothAdapter.STATE_ON:
- assertTrue(adapter.isEnabled());
- return;
- case BluetoothAdapter.STATE_OFF:
- case BluetoothAdapter.STATE_TURNING_OFF:
- assertFalse(adapter.isEnabled());
- assertTrue(adapter.enable());
- break;
- case BluetoothAdapter.STATE_TURNING_ON:
- assertFalse(adapter.isEnabled());
- mask = 0; // Don't check for received intents since we might have missed them.
- break;
- default:
- fail("enable() invalid state: state=" + state);
- }
-
- long s = System.currentTimeMillis();
- while (System.currentTimeMillis() - s < ENABLE_TIMEOUT) {
- state = adapter.getState();
- if (state == BluetoothAdapter.STATE_ON) {
- assertTrue(adapter.isEnabled());
- if ((mReceiver.getFiredFlags() & mask) == mask) {
- mReceiver.resetFiredFlags();
- return;
- }
- } else {
- assertFalse(adapter.isEnabled());
- assertEquals(BluetoothAdapter.STATE_TURNING_ON, state);
- }
- sleep(POLL_TIME);
- }
-
- int firedFlags = mReceiver.getFiredFlags();
- mReceiver.resetFiredFlags();
- fail("enable() timeout: " +
- "state=" + state + " (expected " + BluetoothAdapter.STATE_OFF + ") " +
- "flags=" + firedFlags + " (expected " + mask + ")");
- }
-
- private void discoverable(BluetoothAdapter adapter) {
- int mask = SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG;
- mReceiver.resetFiredFlags();
-
- if (!adapter.isEnabled()) {
- fail("discoverable() bluetooth not enabled");
- }
-
- int scanMode = adapter.getScanMode();
- if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
- return;
- }
-
- assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE);
- assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE));
-
- long s = System.currentTimeMillis();
- while (System.currentTimeMillis() - s < SET_SCAN_MODE_TIMEOUT) {
- scanMode = adapter.getScanMode();
- if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
- if ((mReceiver.getFiredFlags() & mask) == mask) {
- mReceiver.resetFiredFlags();
- return;
- }
- } else {
- assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE);
- }
- sleep(POLL_TIME);
- }
-
- int firedFlags = mReceiver.getFiredFlags();
- mReceiver.resetFiredFlags();
- fail("discoverable() timeout: " +
- "scanMode=" + scanMode + " (expected " +
- BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE + ") " +
- "flags=" + firedFlags + " (expected " + mask + ")");
- }
-
- private void undiscoverable(BluetoothAdapter adapter) {
- int mask = SCAN_MODE_CONNECTABLE_FLAG;
- mReceiver.resetFiredFlags();
-
- if (!adapter.isEnabled()) {
- fail("undiscoverable() bluetooth not enabled");
- }
-
- int scanMode = adapter.getScanMode();
- if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) {
- return;
- }
-
- assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
- assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE));
-
- long s = System.currentTimeMillis();
- while (System.currentTimeMillis() - s < SET_SCAN_MODE_TIMEOUT) {
- scanMode = adapter.getScanMode();
- if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) {
- if ((mReceiver.getFiredFlags() & mask) == mask) {
- mReceiver.resetFiredFlags();
- return;
- }
- } else {
- assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
- }
- sleep(POLL_TIME);
- }
-
- int firedFlags = mReceiver.getFiredFlags();
- mReceiver.resetFiredFlags();
- fail("undiscoverable() timeout: " +
- "scanMode=" + scanMode + " (expected " +
- BluetoothAdapter.SCAN_MODE_CONNECTABLE + ") " +
- "flags=" + firedFlags + " (expected " + mask + ")");
- }
-
- private void startScan(BluetoothAdapter adapter) {
- int mask = DISCOVERY_STARTED_FLAG;
- mReceiver.resetFiredFlags();
-
- if (!adapter.isEnabled()) {
- fail("startScan() bluetooth not enabled");
- }
-
- if (adapter.isDiscovering()) {
- return;
- }
-
- assertTrue(adapter.startDiscovery());
-
- long s = System.currentTimeMillis();
- while (System.currentTimeMillis() - s < START_DISCOVERY_TIMEOUT) {
- if (adapter.isDiscovering() && ((mReceiver.getFiredFlags() & mask) == mask)) {
- mReceiver.resetFiredFlags();
- return;
- }
- sleep(POLL_TIME);
- }
-
- int firedFlags = mReceiver.getFiredFlags();
- mReceiver.resetFiredFlags();
- fail("startScan() timeout: " +
- "isDiscovering=" + adapter.isDiscovering() + " " +
- "flags=" + firedFlags + " (expected " + mask + ")");
- }
-
- private void stopScan(BluetoothAdapter adapter) {
- int mask = DISCOVERY_FINISHED_FLAG;
- mReceiver.resetFiredFlags();
-
- if (!adapter.isEnabled()) {
- fail("stopScan() bluetooth not enabled");
- }
-
- if (!adapter.isDiscovering()) {
- return;
- }
-
- // TODO: put assertTrue() around cancelDiscovery() once it starts
- // returning true.
- adapter.cancelDiscovery();
-
- long s = System.currentTimeMillis();
- while (System.currentTimeMillis() - s < CANCEL_DISCOVERY_TIMEOUT) {
- if (!adapter.isDiscovering() && ((mReceiver.getFiredFlags() & mask) == mask)) {
- mReceiver.resetFiredFlags();
- return;
- }
- sleep(POLL_TIME);
- }
-
- int firedFlags = mReceiver.getFiredFlags();
- mReceiver.resetFiredFlags();
- fail("stopScan() timeout: " +
- "isDiscovering=" + adapter.isDiscovering() + " " +
- "flags=" + firedFlags + " (expected " + mask + ")");
- }
-
- private void sleep(long time) {
- try {
- Thread.sleep(time);
- } catch (InterruptedException e) {
- }
+ mTestUtils.disable(adapter);
}
}
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java b/core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java
new file mode 100644
index 0000000..cf0ff99
--- /dev/null
+++ b/core/tests/coretests/src/android/bluetooth/BluetoothTestRunner.java
@@ -0,0 +1,73 @@
+/*
+ * 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.bluetooth;
+
+import junit.framework.TestSuite;
+
+import android.os.Bundle;
+import android.test.InstrumentationTestRunner;
+import android.test.InstrumentationTestSuite;
+
+public class BluetoothTestRunner extends InstrumentationTestRunner {
+ public static int sEnableIterations = 100;
+ public static int sDiscoverableIterations = 1000;
+ public static int sScanIterations = 1000;
+
+ @Override
+ public TestSuite getAllTests() {
+ TestSuite suite = new InstrumentationTestSuite(this);
+ suite.addTestSuite(BluetoothStressTest.class);
+ return suite;
+ }
+
+ @Override
+ public ClassLoader getLoader() {
+ return BluetoothTestRunner.class.getClassLoader();
+ }
+
+ @Override
+ public void onCreate(Bundle arguments) {
+ super.onCreate(arguments);
+
+ String val = arguments.getString("enable_iterations");
+ if (val != null) {
+ try {
+ sEnableIterations = Integer.parseInt(val);
+ } catch (NumberFormatException e) {
+ // Invalid argument, fall back to default value
+ }
+ }
+
+ val = arguments.getString("discoverable_iterations");
+ if (val != null) {
+ try {
+ sDiscoverableIterations = Integer.parseInt(val);
+ } catch (NumberFormatException e) {
+ // Invalid argument, fall back to default value
+ }
+ }
+
+ val = arguments.getString("scan_iterations");
+ if (val != null) {
+ try {
+ sScanIterations = Integer.parseInt(val);
+ } catch (NumberFormatException e) {
+ // Invalid argument, fall back to default value
+ }
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java
new file mode 100644
index 0000000..82de509
--- /dev/null
+++ b/core/tests/coretests/src/android/bluetooth/BluetoothTestUtils.java
@@ -0,0 +1,441 @@
+/*
+ * 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.bluetooth;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Environment;
+import android.util.Log;
+
+import junit.framework.Assert;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+public class BluetoothTestUtils extends Assert {
+
+ /**
+ * Timeout for {@link BluetoothAdapter#disable()} in ms.
+ */
+ private static final int DISABLE_TIMEOUT = 5000;
+
+ /**
+ * Timeout for {@link BluetoothAdapter#enable()} in ms.
+ */
+ private static final int ENABLE_TIMEOUT = 20000;
+
+ /**
+ * Timeout for {@link BluetoothAdapter#setScanMode(int)} in ms.
+ */
+ private static final int SET_SCAN_MODE_TIMEOUT = 5000;
+
+ /**
+ * Timeout for {@link BluetoothAdapter#startDiscovery()} in ms.
+ */
+ private static final int START_DISCOVERY_TIMEOUT = 5000;
+
+ /**
+ * Timeout for {@link BluetoothAdapter#cancelDiscovery()} in ms.
+ */
+ private static final int CANCEL_DISCOVERY_TIMEOUT = 5000;
+
+ private static final int DISCOVERY_STARTED_FLAG = 1;
+ private static final int DISCOVERY_FINISHED_FLAG = 1 << 1;
+ private static final int SCAN_MODE_NONE_FLAG = 1 << 2;
+ private static final int SCAN_MODE_CONNECTABLE_FLAG = 1 << 3;
+ private static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG = 1 << 4;
+ private static final int STATE_OFF_FLAG = 1 << 5;
+ private static final int STATE_TURNING_ON_FLAG = 1 << 6;
+ private static final int STATE_ON_FLAG = 1 << 7;
+ private static final int STATE_TURNING_OFF_FLAG = 1 << 8;
+
+ /**
+ * Time between polls in ms.
+ */
+ private static final int POLL_TIME = 100;
+
+ private Context mContext;
+
+ private BufferedWriter mOutputWriter;
+
+ private String mOutputFile;
+ private String mTag;
+
+ private class BluetoothReceiver extends BroadcastReceiver {
+ private int mFiredFlags = 0;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (this) {
+ if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(intent.getAction())) {
+ mFiredFlags |= DISCOVERY_STARTED_FLAG;
+ } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(intent.getAction())) {
+ mFiredFlags |= DISCOVERY_FINISHED_FLAG;
+ } else if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(intent.getAction())) {
+ int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE,
+ BluetoothAdapter.ERROR);
+ assertNotSame(mode, BluetoothAdapter.ERROR);
+ switch (mode) {
+ case BluetoothAdapter.SCAN_MODE_NONE:
+ mFiredFlags |= SCAN_MODE_NONE_FLAG;
+ break;
+ case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
+ mFiredFlags |= SCAN_MODE_CONNECTABLE_FLAG;
+ break;
+ case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
+ mFiredFlags |= SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG;
+ break;
+ }
+ } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
+ int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
+ BluetoothAdapter.ERROR);
+ assertNotSame(state, BluetoothAdapter.ERROR);
+ switch (state) {
+ case BluetoothAdapter.STATE_OFF:
+ mFiredFlags |= STATE_OFF_FLAG;
+ break;
+ case BluetoothAdapter.STATE_TURNING_ON:
+ mFiredFlags |= STATE_TURNING_ON_FLAG;
+ break;
+ case BluetoothAdapter.STATE_ON:
+ mFiredFlags |= STATE_ON_FLAG;
+ break;
+ case BluetoothAdapter.STATE_TURNING_OFF:
+ mFiredFlags |= STATE_TURNING_OFF_FLAG;
+ break;
+ }
+ }
+ }
+ }
+
+ public int getFiredFlags() {
+ synchronized (this) {
+ return mFiredFlags;
+ }
+ }
+
+ public void resetFiredFlags() {
+ synchronized (this) {
+ mFiredFlags = 0;
+ }
+ }
+ }
+
+ private BluetoothReceiver mReceiver = new BluetoothReceiver();
+
+ public BluetoothTestUtils(Context context, String tag) {
+ this(context, tag, null);
+ }
+
+ public BluetoothTestUtils(Context context, String tag, String outputFile) {
+ mContext = context;
+ mTag = tag;
+ mOutputFile = outputFile;
+
+ if (mOutputFile == null) {
+ mOutputWriter = null;
+ } else {
+ try {
+ mOutputWriter = new BufferedWriter(new FileWriter(new File(
+ Environment.getExternalStorageDirectory(), mOutputFile), true));
+ } catch (IOException e) {
+ Log.w(mTag, "Test output file could not be opened", e);
+ mOutputWriter = null;
+ }
+ }
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
+ filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
+ filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
+ filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+ mContext.registerReceiver(mReceiver, filter);
+ }
+
+ public void close() {
+ mContext.unregisterReceiver(mReceiver);
+
+ if (mOutputWriter != null) {
+ try {
+ mOutputWriter.close();
+ } catch (IOException e) {
+ Log.w(mTag, "Test output file could not be closed", e);
+ }
+ }
+ }
+
+ public void enable(BluetoothAdapter adapter) {
+ int mask = STATE_TURNING_ON_FLAG | STATE_ON_FLAG | SCAN_MODE_CONNECTABLE_FLAG;
+ mReceiver.resetFiredFlags();
+
+ int state = adapter.getState();
+ switch (state) {
+ case BluetoothAdapter.STATE_ON:
+ assertTrue(adapter.isEnabled());
+ return;
+ case BluetoothAdapter.STATE_OFF:
+ case BluetoothAdapter.STATE_TURNING_OFF:
+ assertFalse(adapter.isEnabled());
+ assertTrue(adapter.enable());
+ break;
+ case BluetoothAdapter.STATE_TURNING_ON:
+ assertFalse(adapter.isEnabled());
+ mask = 0; // Don't check for received intents since we might have missed them.
+ break;
+ default:
+ fail("enable() invalid state: state=" + state);
+ }
+
+ long s = System.currentTimeMillis();
+ while (System.currentTimeMillis() - s < ENABLE_TIMEOUT) {
+ state = adapter.getState();
+ if (state == BluetoothAdapter.STATE_ON) {
+ assertTrue(adapter.isEnabled());
+ if ((mReceiver.getFiredFlags() & mask) == mask) {
+ mReceiver.resetFiredFlags();
+ writeOutput(String.format("enable() completed in %d ms",
+ (System.currentTimeMillis() - s)));
+ return;
+ }
+ } else {
+ assertFalse(adapter.isEnabled());
+ assertEquals(BluetoothAdapter.STATE_TURNING_ON, state);
+ }
+ sleep(POLL_TIME);
+ }
+
+ int firedFlags = mReceiver.getFiredFlags();
+ mReceiver.resetFiredFlags();
+ fail(String.format("enable() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)",
+ state, BluetoothAdapter.STATE_ON, firedFlags, mask));
+ }
+
+ public void disable(BluetoothAdapter adapter) {
+ int mask = STATE_TURNING_OFF_FLAG | STATE_OFF_FLAG | SCAN_MODE_NONE_FLAG;
+ mReceiver.resetFiredFlags();
+
+ int state = adapter.getState();
+ switch (state) {
+ case BluetoothAdapter.STATE_OFF:
+ assertFalse(adapter.isEnabled());
+ return;
+ case BluetoothAdapter.STATE_ON:
+ assertTrue(adapter.isEnabled());
+ assertTrue(adapter.disable());
+ break;
+ case BluetoothAdapter.STATE_TURNING_ON:
+ assertFalse(adapter.isEnabled());
+ assertTrue(adapter.disable());
+ break;
+ case BluetoothAdapter.STATE_TURNING_OFF:
+ assertFalse(adapter.isEnabled());
+ mask = 0; // Don't check for received intents since we might have missed them.
+ break;
+ default:
+ fail("disable() invalid state: state=" + state);
+ }
+
+ long s = System.currentTimeMillis();
+ while (System.currentTimeMillis() - s < DISABLE_TIMEOUT) {
+ state = adapter.getState();
+ if (state == BluetoothAdapter.STATE_OFF) {
+ assertFalse(adapter.isEnabled());
+ if ((mReceiver.getFiredFlags() & mask) == mask) {
+ mReceiver.resetFiredFlags();
+ writeOutput(String.format("disable() completed in %d ms",
+ (System.currentTimeMillis() - s)));
+ return;
+ }
+ } else {
+ assertFalse(adapter.isEnabled());
+ assertEquals(BluetoothAdapter.STATE_TURNING_OFF, state);
+ }
+ sleep(POLL_TIME);
+ }
+
+ int firedFlags = mReceiver.getFiredFlags();
+ mReceiver.resetFiredFlags();
+ fail(String.format("disable() timeout: state=%d (expected %d), flags=0x%x (expected 0x%x)",
+ state, BluetoothAdapter.STATE_OFF, firedFlags, mask));
+ }
+
+ public void discoverable(BluetoothAdapter adapter) {
+ int mask = SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG;
+ mReceiver.resetFiredFlags();
+
+ if (!adapter.isEnabled()) {
+ fail("discoverable() bluetooth not enabled");
+ }
+
+ int scanMode = adapter.getScanMode();
+ if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
+ return;
+ }
+
+ assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE);
+ assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE));
+
+ long s = System.currentTimeMillis();
+ while (System.currentTimeMillis() - s < SET_SCAN_MODE_TIMEOUT) {
+ scanMode = adapter.getScanMode();
+ if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
+ if ((mReceiver.getFiredFlags() & mask) == mask) {
+ mReceiver.resetFiredFlags();
+ writeOutput(String.format("discoverable() completed in %d ms",
+ (System.currentTimeMillis() - s)));
+ return;
+ }
+ } else {
+ assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE);
+ }
+ sleep(POLL_TIME);
+ }
+
+ int firedFlags = mReceiver.getFiredFlags();
+ mReceiver.resetFiredFlags();
+ fail(String.format("discoverable() timeout: scanMode=%d (expected %d), flags=0x%x "
+ + "(expected 0x%x)", scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE,
+ firedFlags, mask));
+ }
+
+ public void undiscoverable(BluetoothAdapter adapter) {
+ int mask = SCAN_MODE_CONNECTABLE_FLAG;
+ mReceiver.resetFiredFlags();
+
+ if (!adapter.isEnabled()) {
+ fail("undiscoverable() bluetooth not enabled");
+ }
+
+ int scanMode = adapter.getScanMode();
+ if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) {
+ return;
+ }
+
+ assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
+ assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE));
+
+ long s = System.currentTimeMillis();
+ while (System.currentTimeMillis() - s < SET_SCAN_MODE_TIMEOUT) {
+ scanMode = adapter.getScanMode();
+ if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) {
+ if ((mReceiver.getFiredFlags() & mask) == mask) {
+ mReceiver.resetFiredFlags();
+ writeOutput(String.format("undiscoverable() completed in %d ms",
+ (System.currentTimeMillis() - s)));
+ return;
+ }
+ } else {
+ assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
+ }
+ sleep(POLL_TIME);
+ }
+
+ int firedFlags = mReceiver.getFiredFlags();
+ mReceiver.resetFiredFlags();
+ fail(String.format("undiscoverable() timeout: scanMode=%d (expected %d), flags=0x%x "
+ + "(expected 0x%x)", scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE, firedFlags,
+ mask));
+ }
+
+ public void startScan(BluetoothAdapter adapter) {
+ int mask = DISCOVERY_STARTED_FLAG;
+ mReceiver.resetFiredFlags();
+
+ if (!adapter.isEnabled()) {
+ fail("startScan() bluetooth not enabled");
+ }
+
+ if (adapter.isDiscovering()) {
+ return;
+ }
+
+ assertTrue(adapter.startDiscovery());
+
+ long s = System.currentTimeMillis();
+ while (System.currentTimeMillis() - s < START_DISCOVERY_TIMEOUT) {
+ if (adapter.isDiscovering() && ((mReceiver.getFiredFlags() & mask) == mask)) {
+ mReceiver.resetFiredFlags();
+ writeOutput(String.format("startScan() completed in %d ms",
+ (System.currentTimeMillis() - s)));
+ return;
+ }
+ sleep(POLL_TIME);
+ }
+
+ int firedFlags = mReceiver.getFiredFlags();
+ mReceiver.resetFiredFlags();
+ fail(String.format("startScan() timeout: isDiscovering=%b, flags=0x%x (expected 0x%x)",
+ adapter.isDiscovering(), firedFlags, mask));
+ }
+
+ public void stopScan(BluetoothAdapter adapter) {
+ int mask = DISCOVERY_FINISHED_FLAG;
+ mReceiver.resetFiredFlags();
+
+ if (!adapter.isEnabled()) {
+ fail("stopScan() bluetooth not enabled");
+ }
+
+ if (!adapter.isDiscovering()) {
+ return;
+ }
+
+ // TODO: put assertTrue() around cancelDiscovery() once it starts returning true.
+ adapter.cancelDiscovery();
+
+ long s = System.currentTimeMillis();
+ while (System.currentTimeMillis() - s < CANCEL_DISCOVERY_TIMEOUT) {
+ if (!adapter.isDiscovering() && ((mReceiver.getFiredFlags() & mask) == mask)) {
+ mReceiver.resetFiredFlags();
+ writeOutput(String.format("stopScan() completed in %d ms",
+ (System.currentTimeMillis() - s)));
+ return;
+ }
+ sleep(POLL_TIME);
+ }
+
+ int firedFlags = mReceiver.getFiredFlags();
+ mReceiver.resetFiredFlags();
+ fail(String.format("stopScan() timeout: isDiscovering=%b, flags=0x%x (expected 0x%x)",
+ adapter.isDiscovering(), firedFlags, mask));
+
+ }
+
+ public void writeOutput(String s) {
+ Log.i(mTag, s);
+ if (mOutputWriter == null) {
+ return;
+ }
+ try {
+ mOutputWriter.write(s + "\n");
+ mOutputWriter.flush();
+ } catch (IOException e) {
+ Log.w(mTag, "Could not write to output file", e);
+ }
+ }
+
+ private void sleep(long time) {
+ try {
+ Thread.sleep(time);
+ } catch (InterruptedException e) {
+ }
+ }
+}
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index d0318cf..35ce17e 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -220,9 +220,16 @@
<li><a style="color:gray;">Accelerometer</a></li>
</ul>
</li> -->
- <li><a href="<?cs var:toroot ?>guide/topics/location/index.html">
- <span class="en">Location and Maps</span>
- </a></li>
+ <li class="toggle-list">
+ <div><a href="<?cs var:toroot ?>guide/topics/location/index.html">
+ <span class="en">Location and Maps</span>
+ </a></div>
+ <ul>
+ <li><a href="<?cs var:toroot ?>guide/topics/location/obtaining-user-location.html">
+ <span class="en">Obtaining User Location</span>
+ </a> <span class="new">new!</span></li>
+ </ul>
+ </li>
<!--<li class="toggle-list">
<div><a style="color:gray;">Wireless Controls</a></div>
<ul>
diff --git a/docs/html/guide/topics/fundamentals.jd b/docs/html/guide/topics/fundamentals.jd
index f780e7c..6d6abd8 100644
--- a/docs/html/guide/topics/fundamentals.jd
+++ b/docs/html/guide/topics/fundamentals.jd
@@ -770,9 +770,9 @@
</p>
<p>
-For more on launch modes, see the description of the
-<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code>
-element.
+For more on launch modes, see the description of the <code><a
+href="{@docRoot}guide/topics/manifest/activity-element.html#lmode"><activity></a></code>
+element.
</p>
diff --git a/docs/html/guide/topics/location/index.jd b/docs/html/guide/topics/location/index.jd
index e988ecb..5f98902 100644
--- a/docs/html/guide/topics/location/index.jd
+++ b/docs/html/guide/topics/location/index.jd
@@ -4,94 +4,63 @@
<div id="qv-wrapper">
<div id="qv">
- <h2>Location and Maps quickview</h2>
+ <h2>Quickview</h2>
<ul>
- <li>Android provides a location framework that your application can use to determine the device's location and bearing and register for updates.</li>
- <li>A Google Maps external library is available that lets you display and manage Maps data.</li>
+ <li>Android provides a location framework that your application can use to determine the
+device's location and bearing and register for updates</li>
+ <li>A Google Maps external library is available that lets you display and manage Maps data</li>
</ul>
- <h2>In this document</h2>
+
+ <h2>Topics</h2>
<ol>
- <li><a href="#location">Location Services</a></li>
- <li><a href="#maps">Google Maps External Library</a></li>
+ <li><a href="{@docRoot}guide/topics/location/obtaining-user-location.html">Obtaining User
+Location</a></li>
</ol>
+
<h2>See Also</h2>
<ol>
- <li><a href="http://code.google.com/android/add-ons/google-apis/index.html">Google APIs add-on download»</a></li>
+ <li><a
+href="http://code.google.com/android/add-ons/google-apis/maps-overview.html">Google
+Maps External Library »</a></li>
</ol>
</div>
</div>
-<p>Location- and maps-based applications and services are compelling for mobile device users. You can build these capabilities into your applications using the classes of the {@link android.location} package and the Google Maps external library. The sections below provide details. </p>
+<p>Location and maps-based applications are compelling for mobile device users. You
+can build these capabilities into your applications using the classes of the {@link
+android.location} package and the Google Maps external library. The sections below provide details.
+</p>
<h2 id="location">Location Services</h2>
<p>Android gives your applications access to the location services supported by
-the device through the classes in the <code>android.location</code> package. The
+the device through the classes in the {@code android.location} package. The
central component of the location framework is the
-{@link android.location.LocationManager} system service, which provides an API to
-determine location and bearing if the underlying device (if it supports location
-capabilities). </p>
+{@link android.location.LocationManager} system service, which provides APIs to
+determine location and bearing of the underlying device (if available). </p>
-<p>As with other system services, you do not instantiate a LocationManager directly.
-Rather, you request an LocationManager instance from the system by calling
-{@link android.content.Context#getSystemService(String) getSystemService(Context.LOCATION_SERVICE)}.
-The method returns a handle to a new LocationManager instance.</p>
+<p>As with other system services, you do not instantiate a {@link android.location.LocationManager}
+directly. Rather, you request an instance from the system by calling
+{@link android.content.Context#getSystemService(String)
+getSystemService(Context.LOCATION_SERVICE)}. The method returns a handle to a new {@link
+android.location.LocationManager} instance.</p>
-<p>Once your application has a handle to a LocationManager instance, your application
-will be able to do three things:</p>
+<p>Once your application has a {@link android.location.LocationManager}, your application
+is able to do three things:</p>
<ul>
- <li>Query for the list of all LocationProviders known to the
- LocationManager for its last known location.</li>
- <li>Register/unregister for periodic updates of current location from a
- LocationProvider (specified either by Criteria or name).</li>
- <li>Register/unregister for a given Intent to be fired if the device comes
- within a given proximity (specified by radius in meters) of a given
- lat/long.</li>
+ <li>Query for the list of all {@link android.location.LocationProvider}s for the last known
+user location.</li>
+ <li>Register/unregister for periodic updates of the user's current location from a
+ location provider (specified either by criteria or name).</li>
+ <li>Register/unregister for a given {@link android.content.Intent} to be fired if the device
+comes within a given proximity (specified by radius in meters) of a given lat/long.</li>
</ul>
-<p>However, during initial development in the emulator, you may not have access to real
-data from a real location provider (Network or GPS). In that case, it may be necessary to
-spoof some data for your application using a mock location provider.</p>
+<p>For more information, read the guide to <a
+href="{@docRoot}guide/topics/location/obtaining-user-location.html">Obtaining User
+Location</a>.</p>
-<p class="note"><strong>Note:</strong> If you've used mock LocationProviders in
-previous versions of the SDK, you can no longer provide canned LocationProviders
-in the /system/etc/location directory. These directories will be wiped during boot-up.
-Please follow the new procedures outlined below.</p>
-
-<h3>Providing Mock Location Data</h3>
-
-<p>When testing your application on the Android emulator, there are a couple different
-ways to send it some mock location data: you can use the DDMS tool or the "geo" command
-option in the emulator console.</p>
-
-<h4 id="ddms">Using DDMS</h4>
-<p>With the DDMS tool, you can simulate location data a few different ways:</p>
-<ul>
- <li>Manually send individual longitude/latitude coordinates to the device.</li>
- <li>Use a GPX file describing a route for playback to the device.</li>
- <li>Use a KML file describing individual placemarks for sequenced playback to the device.</li>
-</ul>
-<p>For more information on using DDMS to spoof location data, see the
-<a href="{@docRoot}guide/developing/tools/ddms.html#emulator-control">Using DDMS guide</a>.
-
-<h4 id="geo">Using the "geo" command in the emulator console</h4>
-<p>Launch your application in the Android emulator and open a terminal/console in
-your SDK's <code>/tools</code> directory. Connect to the emulator console. Now you can use:</p>
-<ul><li><code>geo fix</code> to send a fixed geo-location.
- <p>This command accepts a longitude and latitude in decimal degrees, and
- an optional altitude in meters. For example:</p>
- <pre>geo fix -121.45356 46.51119 4392</pre>
- </li>
- <li><code>geo nmea</code> to send an NMEA 0183 sentence.
- <p>This command accepts a single NMEA sentence of type '$GPGGA' (fix data) or '$GPRMC' (transit data).
- For example:</p>
- <pre>geo nmea $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62</pre>
- </li>
-</ul>
-
-<p>For information about how to connect to the emulator console, see
-<a href="{@docRoot}guide/developing/tools/emulator.html#console">Using the Emulator Console</a>.</p>
<h2 id="maps">Google Maps External Library</h2>
@@ -128,9 +97,9 @@
<p style="margin-left:2em;"><a
href="http://code.google.com/android/add-ons/google-apis">http://code.google.com/android/add-ons/google-apis</a></p>
-<p>For your convenience, the Google APIs add-on is also included in the Android
-SDK. <!-- To learn now to use the Maps external library in your application, see
-[[Using External Libraries]].--></p>
+<p>For your convenience, the Google APIs add-on is also available as a downloadable component from
+the Android SDK and AVD Manager (see <a href="{@docRoot}sdk/adding-components.html">Adding SDK
+Components</a>).</p>
<p class="note"><strong>Note:</strong> In order to display Google Maps data in a
MapView, you must register with the Google Maps service and obtain a Maps API
diff --git a/docs/html/guide/topics/location/obtaining-user-location.jd b/docs/html/guide/topics/location/obtaining-user-location.jd
new file mode 100644
index 0000000..bc782d2
--- /dev/null
+++ b/docs/html/guide/topics/location/obtaining-user-location.jd
@@ -0,0 +1,454 @@
+page.title=Obtaining User Location
+parent.title=Location and Maps
+parent.link=index.html
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+
+ <h2>Quickview</h2>
+ <ul>
+ <li>The Network Location Provider provides good location data without using GPS</li>
+ <li>Obtaining user location can consume a lot of battery, so be careful how
+long you listen for updates</li>
+ </ul>
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#Challenges">Challenges in Determining User Location</a></li>
+ <li><a href="#Updates">Requesting Location Updates</a>
+ <ol>
+ <li><a href="#Permission">Requesting User Permissions</a></li>
+ </ol>
+ </li>
+ <li><a href="#BestPerformance">Defining a Model for the Best Performance</a>
+ <ol>
+ <li><a href="#Flow">Flow for obtaining user location</a></li>
+ <li><a href="#StartListening">Deciding when to start listening for updates</a></li>
+ <li><a href="#FastFix">Getting a fast fix with the last known location</a></li>
+ <li><a href="#StopListening">Deciding when to stop listening for updates</a></li>
+ <li><a href="#BestEstimate">Maintaining a current best estimate</a></li>
+ <li><a href="#Adjusting">Adjusting the model to save battery and data exchange</a></li>
+ </ol>
+ </li>
+ <li><a href="#MockData">Providing Mock Location Data</a></li>
+ </ol>
+ <h2>Key classes</h2>
+ <ol>
+ <li>{@link android.location.LocationManager}</li>
+ <li>{@link android.location.LocationListener}</li>
+ </ol>
+</div>
+</div>
+
+ <p>Knowing where the user is allows your application to be smarter and deliver
+better information to the user. When developing a location-aware application for Android, you can
+utilize GPS and Android's Network Location Provider to acquire the user location. Although
+GPS is most accurate, it only works outdoors, it quickly consumes battery power, and doesn't return
+the location as quickly as users want. Android's Network Location Provider determines user location
+using cell tower and Wi-Fi signals, providing location information in a way that
+works indoors and outdoors, responds faster, and uses less battery power. To obtain the user
+location in your application, you can use both GPS and the Network Location Provider, or just
+one.</p>
+
+
+<h2 id="Challenges">Challenges in Determining User Location</h2>
+
+<p>Obtaining user location from a mobile device can be complicated. There are several reasons
+why a location reading (regardless of the source) can contain errors and be inaccurate.
+Some sources of error in the user location include:</p>
+
+<ul>
+ <li><b>Multitude of location sources</b>
+ <p>GPS, Cell-ID, and Wi-Fi can each provide a clue to users location. Determining which to use
+and trust is a matter of trade-offs in accuracy, speed, and battery-efficiency.</p>
+ </li>
+ <li><b>User movement</b>
+ <p>Because the user location changes, you must account for movement by re-estimating user
+location every so often.</p>
+ </li>
+ <li><b>Varying accuracy</b>
+ <p>Location estimates coming from each location source are not consistent in their
+accuracy. A location obtained 10 seconds ago from one source might be more accurate than the newest
+location from another or same source.</p>
+ </li>
+</ul>
+
+ <p>These problems can make it difficult to obtain a reliable user location reading. This
+document provides information to help you meet these challenges to obtain a reliable location
+reading. It also provides ideas that you can use in your
+application to provide the user with an accurate and responsive geo-location experience.</p>
+
+
+<h2 id="Updates">Requesting Location Updates</h2>
+
+ <p>Before addressing some of the location errors described above, here is an introduction to
+how you can obtain user location on Android.</p>
+
+ <p>Getting user location in Android works by means of callback. You indicate that you'd
+like to receive location updates from the {@link android.location.LocationManager} ("Location
+Manager") by calling {@link android.location.LocationManager#requestLocationUpdates
+requestLocationUpdates()}, passing it a
+{@link android.location.LocationListener}. Your {@link android.location.LocationListener} must
+implement several callback methods that the Location Manager calls when the user location
+changes or when the status of the service changes.</p>
+
+<p>For example, the following code shows how to define a {@link android.location.LocationListener}
+and request location updates:
+ </p>
+
+<pre>
+// Acquire a reference to the system Location Manager
+LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
+
+// Define a listener that responds to location updates
+LocationListener locationListener = new LocationListener() {
+ public void onLocationChanged(Location location) {
+ // Called when a new location is found by the network location provider.
+ makeUseOfNewLocation(location);
+ }
+
+ public void onStatusChanged(String provider, int status, Bundle extras) {}
+
+ public void onProviderEnabled(String provider) {}
+
+ public void onProviderDisabled(String provider) {}
+ };
+
+// Register the listener with the Location Manager to receive location updates
+locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
+</pre>
+
+ <p>The first parameter in {@link
+android.location.LocationManager#requestLocationUpdates requestLocationUpdates()} is the type of
+location provider to use (in this case, the Network Location Provider for cell tower and Wi-Fi
+based location). You can control the frequency at which your listener receives updates
+with the second and third parameter—the second is the minimum time interval between
+notifications and the third is the minimum change in distance between notifications—setting
+both to zero requests location notifications as frequently as possible. The last parameter is your
+{@link android.location.LocationListener}, which receives callbacks for location updates.</p>
+
+<p>To request location updates from the GPS provider,
+substitute <code>GPS_PROVIDER</code> for <code>NETWORK_PROVIDER</code>. You can also request
+location updates from both the GPS and the Network Location Provider by calling {@link
+android.location.LocationManager#requestLocationUpdates requestLocationUpdates()} twice—once
+for <code>NETWORK_PROVIDER</code> and once for <code>GPS_PROVIDER</code>.</p>
+
+
+<h3 id="Permission">Requesting User Permissions</h3>
+
+<p>In order to receive location updates from <code>NETWORK_PROVIDER</code> or
+<code>GPS_PROVIDER</code>, you must request user permission by declaring either the {@code
+ACCESS_COARSE_LOCATION} or {@code ACCESS_FINE_LOCATION} permission, respectively, in your Android
+manifest file. For example:</p>
+
+<pre>
+<manifest ... >
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ ...
+</manifest>
+</pre>
+
+<p>Without these permissions, your application will fail at runtime when requesting
+location updates.</p>
+
+<p class="note"><strong>Note:</strong> If you are using both <code>NETWORK_PROVIDER</code> and
+<code>GPS_PROVIDER</code>, then you need to request only the {@code ACCESS_FINE_LOCATION}
+permission, because it includes permission for both providers. (Permission for {@code
+ACCESS_COARSE_LOCATION} includes permission only for <code>NETWORK_PROVIDER</code>.)</p>
+
+
+<h2 id="BestPerformance">Defining a Model for the Best Performance</h2>
+
+ <p>Location-based applications are now commonplace, but due to the less than optimal
+accuracy, user movement, the multitude of methods to obtain the location, and the desire to conserve
+battery, getting user location is complicated. To overcome the obstacles of obtaining a good user
+location while preserving battery power, you must define a consistent model that specifies how your
+application obtains the user location. This model includes when you start and stop listening for
+updates and when to use cached location data.</p>
+
+
+ <h3 id="Flow">Flow for obtaining user location</h3>
+
+ <p>Here's the typical flow of procedures for obtaining the user location:</p>
+
+ <ol>
+ <li>Start application.</li>
+ <li>Sometime later, start listening for updates from desired location providers.</li>
+ <li>Maintain a "current best estimate" of location by filtering out new, but less accurate
+fixes.</li>
+ <li>Stop listening for location updates.</li>
+ <li>Take advantage of the last best location estimate.</li>
+ </ol>
+
+ <p>Figure 1 demonstrates this model in a timeline that visualizes the period in which an
+application is listening for location updates and the events that occur during that time.</p>
+
+<img src="{@docRoot}images/location/getting-location.png" alt="" />
+<p class="img-caption"><strong>Figure 1.</strong> A timeline representing the window in which an
+application listens for location updates.</p>
+
+ <p>This model of a window—during which location updates are received—frames many of
+the decisions you need to make when adding location-based services to your application.</p>
+
+
+ <h3 id="StartListening">Deciding when to start listening for updates</h3>
+
+ <p>You might want to start listening for location updates as soon as your application starts, or
+only after users activate a certain feature. Be aware that long windows of listening for location
+fixes can consume a lot of battery power, but short periods might not allow for sufficient
+accuracy.</p>
+
+ <p>As demonstrated above, you can begin listening for updates by calling {@link
+android.location.LocationManager#requestLocationUpdates requestLocationUpdates()}:</p>
+
+<pre>
+LocationProvider locationProvider = LocationManager.NETWORK_PROVIDER;
+// Or, use GPS location data:
+// LocationProvider locationProvider = LocationManager.GPS_PROVIDER;
+
+locationManager.requestLocationUpdates(locationProvider, 0, 0, locationListener);
+</pre>
+
+
+ <h3 id="FastFix">Getting a fast fix with the last known location</h3>
+
+ <p>The time it takes for your location listener to receive the first location fix is often too
+long for users wait. Until a more accurate location is provided to your location listener, you
+should utilize a cached location by calling {@link
+android.location.LocationManager#getLastKnownLocation}:</p>
+<pre>
+LocationProvider locationProvider = LocationManager.NETWORK_PROVIDER;
+// Or use LocationManager.GPS_PROVIDER
+
+Location lastKnownLocation = locationManager.getLastKnownLocation(locationProvider);
+</pre>
+
+
+ <h3 id="StopListening">Deciding when to stop listening for updates</h3>
+
+ <p>The logic of deciding when new fixes are no longer necessary might range from very simple to
+very complex depending on your application. A short gap between when the location is acquired and
+when the location is used, improves the accuracy of the estimate. Always beware that listening for a
+long time consumes a lot of battery power, so as soon as you have the information you need, you
+should stop
+listening for updates by calling {@link android.location.LocationManager#removeUpdates}:</p>
+<pre>
+// Remove the listener you previously added
+locationManager.removeUpdates(locationListener);
+</pre>
+
+
+ <h3 id="BestEstimate">Maintaining a current best estimate</h3>
+
+ <p>You might expect that the most recent location fix is the most accurate.
+However, because the accuracy of a location fix varies, the most recent fix is not always the best.
+You should include logic for choosing location fixes based on several criteria. The criteria also
+varies depending on the use-cases of the application and field testing.</p>
+
+ <p>Here are a few steps you can take to validate the accuracy of a location fix:</p>
+ <ul>
+ <li>Check if the location retrieved is significantly newer than the previous estimate.</li>
+ <li>Check if the accuracy claimed by the location is better or worse than the previous
+estimate.</li>
+ <li>Check which provider the new location is from and determine if you trust it more.</li>
+ </ul>
+
+ <p>An elaborate example of this logic can look something like this:</p>
+
+<pre>
+private static final int TWO_MINUTES = 1000 * 60 * 2;
+
+/** Determines whether one Location reading is better than the current Location fix
+ * @param location The new Location that you want to evaluate
+ * @param currentBestLocation The current Location fix, to which you want to compare the new one
+ */
+protected boolean isBetterLocation(Location location, Location currentBestLocation) {
+ if (currentBestLocation == null) {
+ // A new location is always better than no location
+ return true;
+ }
+
+ // Check whether the new location fix is newer or older
+ long timeDelta = location.getTime() - currentBestLocation.getTime();
+ boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
+ boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
+ boolean isNewer = timeDelta > 0;
+
+ // If it's been more than two minutes since the current location, use the new location
+ // because the user has likely moved
+ if (isSignificantlyNewer) {
+ return true;
+ // If the new location is more than two minutes older, it must be worse
+ } else if (isSignificantlyOlder) {
+ return false;
+ }
+
+ // Check whether the new location fix is more or less accurate
+ int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
+ boolean isLessAccurate = accuracyDelta > 0;
+ boolean isMoreAccurate = accuracyDelta < 0;
+ boolean isSignificantlyLessAccurate = accuracyDelta > 200;
+
+ // Check if the old and new location are from the same provider
+ boolean isFromSameProvider = isSameProvider(location.getProvider(),
+ currentBestLocation.getProvider());
+
+ // Determine location quality using a combination of timeliness and accuracy
+ if (isMoreAccurate) {
+ return true;
+ } else if (isNewer && !isLessAccurate) {
+ return true;
+ } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
+ return true;
+ }
+ return false;
+}
+
+/** Checks whether two providers are the same */
+private boolean isSameProvider(String provider1, String provider2) {
+ if (provider1 == null) {
+ return provider2 == null;
+ }
+ return provider1.equals(provider2);
+}
+</pre>
+
+
+ <h3 id="Adjusting">Adjusting the model to save battery and data exchange</h3>
+
+ <p>As you test your application, you might find that your model for providing good location and
+good performance needs some adjustment. Here are some things you might change to find a good
+balance between the two.</p>
+
+ <h4>Reduce the size of the window</h4>
+
+ <p>A smaller window in which you listen for location updates means less interaction with GPS and
+network location services, thus, preserving battery life. But it also allows for fewer locations
+from which to choose a best estimate.</p>
+
+ <h4>Set the location providers to return updates less frequently</h4>
+
+ <p>Reducing the rate at which new updates appear during the window can also improve battery
+efficiency, but at the cost of accuracy. The value of the trade-off depends on how your
+application is used. You can reduce the rate of updates by increasing the parameters in {@link
+android.location.LocationManager#requestLocationUpdates requestLocationUpdates()} that specify the
+interval time and minimum distance change.</p>
+
+ <h4>Restrict a set of providers</h4>
+
+ <p>Depending on the environment where your application is used or the desired level of accuracy,
+you might choose to use only the Network Location Provider or only GPS, instead of both. Interacting
+with only one of the services reduces battery usage at a potential cost of accuracy.</p>
+
+
+ <h2>Common application cases</h2>
+
+ <p>There are many reasons you might want to obtain the user location in your application. Below
+are a couple scenarios in which you can use the user location to enrich your application. Each
+scenario also describes good practices for when you should start and stop listening for the
+location, in order to get a good reading and help preserve battery life.</p>
+
+
+ <h3>Tagging user-created content with a location</h3>
+
+ <p>You might be creating an application where user-created content is tagged with a location.
+Think of users sharing their local experiences, posting a review for a restaurant, or recording some
+content that can be augmented with their current location. A model of how this
+interaction might happen, with respect to the location services, is visualized in figure 2.</p>
+
+ <img src="{@docRoot}images/location/content-tagging.png" alt="" />
+<p class="img-caption"><strong>Figure 2.</strong> A timeline representing the window in which
+the user location is obtained and listening stops when the user consumes the current location.</p>
+
+ <p>This lines up with the previous model of how user location is obtained in code (figure 1). For
+best location accuracy, you might choose to start listening for location updates when users begin
+creating
+the content or even when the application starts, then stop listening for updates when content is
+ready to be posted or recorded. You might need to consider how long a typical task of creating the
+content takes and judge if this duration allows for efficient collection of a location estimate.</p>
+
+
+ <h3>Helping the user decide on where to go</h3>
+
+ <p>You might be creating an application that attempts to provide users with a set
+of options about where to go. For example, you're looking to provide a list of nearby restaurants,
+stores, and entertainment and the order of recommendations changes depending on the user
+location.</p>
+
+ <p>To accommodate such a flow, you might choose to:</p>
+ <ul>
+ <li>Rearrange recommendations when a new best estimate is obtained</li>
+ <li>Stop listening for updates if the order of recommendations has stabilized</li>
+ </ul>
+
+ <p>This kind of model is visualized in figure 3.</p>
+
+ <img src="{@docRoot}images/location/where-to-go.png" alt="" />
+<p class="img-caption"><strong>Figure 3.</strong> A timeline representing the window in which a
+dynamic set of data is updated each time the user location updates.</p>
+
+
+
+
+<h2 id="MockData">Providing Mock Location Data</h2>
+
+<p>As you develop your application, you'll certainly need to test how well your model for obtaining
+user location works. This is most easily done using a real Android-powered device. If, however, you
+don't have a device, you can still test your location-based features by mocking location data in
+the Android emulator. There are three different ways to send your application mock location
+data: using Eclipse, DDMS, or the "geo" command in the emulator console.</p>
+
+<p class="note"><strong>Note:</strong> Providing mock location data is injected as GPS location
+data, so you must request location updates from <code>GPS_PROVIDER</code> in order for mock location
+data to work.</p>
+
+<h3 id="MockEclipse">Using Eclipse</h3>
+
+<p>Select <b>Window</b> > <b>Show View</b> > <b>Other</b> > <b>Emulator Control</b>.</p>
+
+<p>In the Emulator Control panel, enter GPS coordinates under Location Controls as individual
+lat/long coordinates, with a GPX file for route playback, or a KML file for multiple place marks.
+(Be sure that you have a device selected in the Devices panel—available from <b>Window</b>
+> <b>Show View</b> > <b>Other</b> > <b>Devices</b>.)</p>
+
+
+<h3 id="MockDdms">Using DDMS</h3>
+
+<p>With the DDMS tool, you can simulate location data a few different ways:</p>
+<ul>
+ <li>Manually send individual longitude/latitude coordinates to the device.</li>
+ <li>Use a GPX file describing a route for playback to the device.</li>
+ <li>Use a KML file describing individual place marks for sequenced playback to the device.</li>
+</ul>
+
+<p>For more information on using DDMS to spoof location data, see the
+<a href="{@docRoot}guide/developing/tools/ddms.html#emulator-control">Using DDMS guide</a>.
+
+
+<h3 id="MockGeo">Using the "geo" command in the emulator console</h3>
+
+<p>To send mock location data from the command line:</p>
+
+<ol>
+ <li>Launch your application in the Android emulator and open a terminal/console in your SDK's
+<code>/tools</code> directory.</li>
+ <li>Connect to the emulator console:
+<pre>telnet localhost <em><console-port></em></pre></li>
+ <li>Send the location data:</p>
+ <ul><li><code>geo fix</code> to send a fixed geo-location.
+ <p>This command accepts a longitude and latitude in decimal degrees, and
+ an optional altitude in meters. For example:</p>
+ <pre>geo fix -121.45356 46.51119 4392</pre>
+ </li>
+ <li><code>geo nmea</code> to send an NMEA 0183 sentence.
+ <p>This command accepts a single NMEA sentence of type '$GPGGA' (fix data) or '$GPRMC' (transit
+ data).
+ For example:</p>
+ <pre>geo nmea $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62</pre>
+ </li>
+ </ul>
+ </li>
+</ol>
+
+<p>For information about how to connect to the emulator console, see
+<a href="{@docRoot}guide/developing/tools/emulator.html#console">Using the Emulator Console</a>.</p>
diff --git a/docs/html/guide/topics/manifest/activity-element.jd b/docs/html/guide/topics/manifest/activity-element.jd
index de8ca6d..e030a4c 100644
--- a/docs/html/guide/topics/manifest/activity-element.jd
+++ b/docs/html/guide/topics/manifest/activity-element.jd
@@ -336,10 +336,10 @@
</p></dd>
<dt><a name="lmode"></a>{@code android:launchMode}</dt>
-<dd>An instruction on how the activity should be launched. There are four modes
+<dd>An instruction on how the activity should be launched. There are four modes
that work in conjunction with activity flags ({@code FLAG_ACTIVITY_*} constants)
-in {@link android.content.Intent} objects to determine what should happen when
-the activity is called upon to handle an intent. They are:
+in {@link android.content.Intent} objects to determine what should happen when
+the activity is called upon to handle an intent. They are:</p>
<p style="margin-left: 2em">"{@code standard}"
<br>"{@code singleTop}"
@@ -351,56 +351,110 @@
</p>
<p>
-The modes fall into two main groups, with "{@code standard}" and
-"{@code singleTop}" activities on one side, and "{@code singleTask}" and
-"{@code singleInstance}" activities on the other. An activity with the
-"{@code standard}" or "{@code singleTop}" launch mode can be instantiated
-multiple times. The instances can belong to any task and can be located
-anywhere in the activity stack. Typically, they're launched into the task
-that called
+As shown in the table below, the modes fall into two main groups, with
+"{@code standard}" and "{@code singleTop}" activities on one side, and
+"{@code singleTask}" and "{@code singleInstance}" activities on the other.
+An activity with the "{@code standard}" or "{@code singleTop}" launch mode
+can be instantiated multiple times. The instances can belong to any task
+and can be located anywhere in the activity stack. Typically, they're
+launched into the task that called
<code>{@link android.content.Context#startActivity startActivity()}</code>
-(unless the Intent object contains a
-<code>{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}</code>
-instruction, in which case a different task is chosen — see the
+(unless the Intent object contains a
+<code>{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}</code>
+instruction, in which case a different task is chosen — see the
<a href="#aff">taskAffinity</a> attribute).
</p>
<p>
-In contrast, "{@code singleTask}" and "{@code singleInstance}" activities
-can only begin a task. They are always at the root of the activity stack.
-Moreover, the device can hold only one instance of the activity at a time
+In contrast, "<code>singleTask</code>" and "<code>singleInstance</code>" activities
+can only begin a task. They are always at the root of the activity stack.
+Moreover, the device can hold only one instance of the activity at a time
— only one such task.
</p>
<p>
The "{@code standard}" and "{@code singleTop}" modes differ from each other
-in just one respect: Every time there's new intent for a "{@code standard}"
-activity, a new instance of the class is created to respond to that intent.
+in just one respect: Every time there's a new intent for a "{@code standard}"
+activity, a new instance of the class is created to respond to that intent.
Each instance handles a single intent.
-Similarly, a new instance of a "{@code singleTop}" activity may also be
-created to handle a new intent. However, if the target task already has an
-existing instance of the activity at the top of its stack, that instance
-will receive the new intent (in an
+Similarly, a new instance of a "{@code singleTop}" activity may also be
+created to handle a new intent. However, if the target task already has an
+existing instance of the activity at the top of its stack, that instance
+will receive the new intent (in an
<code>{@link android.app.Activity#onNewIntent onNewIntent()}</code> call);
a new instance is not created.
-In other circumstances — for example, if an existing instance of the
-"{@code singleTop}" activity is in the target task, but not at the top of
-the stack, or if it's at the top of a stack, but not in the target task
+In other circumstances — for example, if an existing instance of the
+"{@code singleTop}" activity is in the target task, but not at the top of
+the stack, or if it's at the top of a stack, but not in the target task
— a new instance would be created and pushed on the stack.
-</p>
+</p>
<p>
-The "{@code singleTask}" and "{@code singleInstance}" modes also differ from
-each other in only one respect: A "{@code singleTask}" activity allows other
-activities to be part of its task. It's at the root of the activity stack,
-but other activities (necessarily "{@code standard}" and "{@code singleTop}"
-activities) can be launched into the same task. A "{@code singleInstance}"
-activity, on the other hand, permits no other activities to be part of its
-task. It's the only activity in the task. If it starts another activity,
-that activity is assigned to a different task — as if {@code
+The "{@code singleTask}" and "{@code singleInstance}" modes also differ from
+each other in only one respect: A "{@code singleTask}" activity allows other
+activities to be part of its task. It's always at the root of its task, but
+other activities (necessarily "{@code standard}" and "{@code singleTop}"
+activities) can be launched into that task. A "{@code singleInstance}"
+activity, on the other hand, permits no other activities to be part of its task.
+It's the only activity in the task. If it starts another activity, that
+activity is assigned to a different task — as if {@code
FLAG_ACTIVITY_NEW_TASK} was in the intent.
</p>
+<table>
+<tr>
+<th>Use Cases</th>
+<th>Launch Mode</th>
+<th>Multiple Instances?</th>
+<th>Comments</th>
+</tr>
+<tr>
+<td rowspan="2" style="width:20%;">Normal launches for most activities</td>
+<td>"<code>standard</code>"</td>
+<td>Yes</td>
+<td>Default. The system always creates a new instance of the activity in the
+target task and routes the intent to it.</td>
+</tr>
+<tr>
+<td>"<code>singleTop</code>"</td>
+<td>Conditionally</td>
+<td>If an instance of the activity already exists at the top of the target task,
+the system routes the intent to that instance through a call to its {@link
+android.app.Activity#onNewIntent onNewIntent()} method, rather than creating a
+new instance of the activity.</td>
+</tr>
+<tr>
+<td rowspan="2">Specialized launches<br>
+<em>(not recommended for general use)</em></td>
+<td>"<code>singleTask</code>"</td>
+<td>No</td>
+<td>The system creates the activity at the root of a new task and routes the
+intent to it. However, if an instance of the activity already exists, the system
+routes the intent to existing instance through a call to its {@link
+android.app.Activity#onNewIntent onNewIntent()} method, rather than creating a
+new one.</td>
+</tr>
+<tr>
+<td>"<code>singleInstance</code>"</td>
+<td>No</td>
+<td>Same as "<code>singleTask"</code>, except that the system doesn't launch any
+other activities into the task holding the instance. The activity is always the
+single and only member of its task.</td>
+</tr>
+</table>
+
+<p>As shown in the table above, <code>standard</code> is the default mode and is
+appropriate for most types of activities. <code>SingleTop</code> is also a
+common and useful launch mode for many types of activities. The other modes
+— <code>singleTask</code> and <code>singleInstance</code> — are
+<span style="color:red">not appropriate for most applications</span>,
+since they result in an interaction model that is likely to be unfamiliar to
+users and is very different from most other applications.
+
+<p>Regardless of the launch mode that you choose, make sure to test the usability
+of the activity during launch and when navigating back to it from
+other activities and tasks using the BACK key. </p>
+
<p>For more information on launch modes and their interaction with Intent
flags, see the
<a href="{@docRoot}guide/topics/fundamentals.html#acttask">Activities and
diff --git a/docs/html/guide/topics/providers/content-providers.jd b/docs/html/guide/topics/providers/content-providers.jd
index 30f8d8c..da4e7a1 100644
--- a/docs/html/guide/topics/providers/content-providers.jd
+++ b/docs/html/guide/topics/providers/content-providers.jd
@@ -779,7 +779,7 @@
requested. Here is the general format for each type:</p></li>
<ul>
-<li><p>For a single record: {@code vnd.android.cursor.item/vnd.<em>yourcompanyname.contenttype</em}</p>
+<li><p>For a single record: {@code vnd.android.cursor.item/vnd.<em>yourcompanyname.contenttype</em>}</p>
<p>For example, a request for train record 122, like this URI,</p>
<p style="margin-left: 2em">{@code content://com.example.transportationprovider/trains/122}</p>
diff --git a/docs/html/images/location/content-tagging.png b/docs/html/images/location/content-tagging.png
new file mode 100644
index 0000000..d58bfee
--- /dev/null
+++ b/docs/html/images/location/content-tagging.png
Binary files differ
diff --git a/docs/html/images/location/getting-location.png b/docs/html/images/location/getting-location.png
new file mode 100644
index 0000000..a5905ec
--- /dev/null
+++ b/docs/html/images/location/getting-location.png
Binary files differ
diff --git a/docs/html/images/location/where-to-go.png b/docs/html/images/location/where-to-go.png
new file mode 100644
index 0000000..59f5983
--- /dev/null
+++ b/docs/html/images/location/where-to-go.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/hello-formstuff.jd b/docs/html/resources/tutorials/views/hello-formstuff.jd
index 3dd5f21..b9f6c16 100644
--- a/docs/html/resources/tutorials/views/hello-formstuff.jd
+++ b/docs/html/resources/tutorials/views/hello-formstuff.jd
@@ -32,9 +32,19 @@
}
</pre>
+<p>Now select which kind of form widget you'd like to create:</p>
+<ul>
+ <li><a href="#CustomButton">Custom Button</a></li>
+ <li><a href="#EditText">Edit Text</a></li>
+ <li><a href="#Checkbox">Checkbox</a></li>
+ <li><a href="#RadioButtons">Radio Buttons</a></li>
+ <li><a href="#ToggleButton">Toggle Button</a></li>
+ <li><a href="#RatingBar">Rating Bar</a></li>
+</ul>
-<h2>Custom Button</h2>
+
+<h2 id="CustomButton">Custom Button</h2>
<p>In this section, you will create a button with a custom image instead of text, using the {@link
android.widget.Button} widget and an XML file that defines three different images to use for the
@@ -111,7 +121,8 @@
</ol>
-<h2>EditText</h2>
+
+<h2 id="EditText">Edit Text</h2>
<p>In this section, you will create a text field for user input, using the {@link
android.widget.EditText} widget. Once text has been entered into the field, the "Enter" key will
@@ -158,7 +169,8 @@
</ol>
-<h2>CheckBox</h2>
+
+<h2 id="Checkbox">Checkbox</h2>
<p>In this section, you will create a checkbox for selecting items, using the {@link
android.widget.CheckBox} widget. When the checkbox is pressed, a toast message will
@@ -209,7 +221,8 @@
android.widget.CompoundButton#toggle()} method.</p>
-<h2>RadioButton</h2>
+
+<h2 id="RadioButtons">Radio Buttons</h2>
<p>In this section, you will create two mutually-exclusive radio buttons (enabling one disables
the other), using the {@link android.widget.RadioGroup} and {@link android.widget.RadioButton}
@@ -274,7 +287,8 @@
android.widget.CompoundButton#toggle()} method.</p>
-<h2>ToggleButton</h2>
+
+<h2 id="ToggleButton">Toggle Button</h2>
<p>In this section, you'll create a button used specifically for toggling between two
states, using the {@link android.widget.ToggleButton} widget. This widget is an excellent
@@ -330,7 +344,7 @@
-<h2>RatingBar</h2>
+<h2 id="RatingBar">Rating Bar</h2>
<p>In this section, you'll create a widget that allows the user to provide a rating,
with the {@link android.widget.RatingBar} widget.</p>
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 185d72a9..8349fe6 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -47,7 +47,8 @@
<bool name="def_networks_available_notification_on">true</bool>
<bool name="def_backup_enabled">false</bool>
- <string name="def_backup_transport" translatable="false"></string>
+ <string name="def_backup_transport" translatable="false">android/com.android.internal.backup.LocalTransport</string>
+
<!-- Default value for whether or not to pulse the notification LED when there is a
pending notification -->
<bool name="def_notification_pulse">true</bool>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 4372cd8..5434694 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -222,16 +222,11 @@
final String value = c.moveToNext() ? c.getString(0) : null;
if (value == null) {
final SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
- String serial = SystemProperties.get("ro.serialno");
- if (serial != null) {
- try {
- random.setSeed(serial.getBytes("UTF-8"));
- } catch (UnsupportedEncodingException ignore) {
- // stick with default seed
- }
- }
+ String serial = SystemProperties.get("ro.serialno", "");
+ random.setSeed(
+ (serial + System.nanoTime() + new SecureRandom().nextLong()).getBytes());
final String newAndroidIdValue = Long.toHexString(random.nextLong());
- Log.d(TAG, "Generated and saved new ANDROID_ID");
+ Log.d(TAG, "Generated and saved new ANDROID_ID [" + newAndroidIdValue + "]");
final ContentValues values = new ContentValues();
values.put(Settings.NameValueTable.NAME, Settings.Secure.ANDROID_ID);
values.put(Settings.NameValueTable.VALUE, newAndroidIdValue);
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 6ceeb95..6c2f1b2 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -642,10 +642,21 @@
}
private boolean doGetShareMethodAvailable(String method) {
- ArrayList<String> rsp = mConnector.doCommand("share status " + method);
+ ArrayList<String> rsp;
+ try {
+ rsp = mConnector.doCommand("share status " + method);
+ } catch (NativeDaemonConnectorException ex) {
+ Slog.e(TAG, "Failed to determine whether share method " + method + " is available.");
+ return false;
+ }
for (String line : rsp) {
- String []tok = line.split(" ");
+ String[] tok = line.split(" ");
+ if (tok.length < 3) {
+ Slog.e(TAG, "Malformed response to share status " + method);
+ return false;
+ }
+
int code;
try {
code = Integer.parseInt(tok[0]);
@@ -770,10 +781,22 @@
private boolean doGetVolumeShared(String path, String method) {
String cmd = String.format("volume shared %s %s", path, method);
- ArrayList<String> rsp = mConnector.doCommand(cmd);
+ ArrayList<String> rsp;
+
+ try {
+ rsp = mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException ex) {
+ Slog.e(TAG, "Failed to read response to volume shared " + path + " " + method);
+ return false;
+ }
for (String line : rsp) {
- String []tok = line.split(" ");
+ String[] tok = line.split(" ");
+ if (tok.length < 3) {
+ Slog.e(TAG, "Malformed response to volume shared " + path + " " + method + " command");
+ return false;
+ }
+
int code;
try {
code = Integer.parseInt(tok[0]);
@@ -782,9 +805,7 @@
return false;
}
if (code == VoldResponseCode.ShareEnabledResult) {
- if (tok[2].equals("enabled"))
- return true;
- return false;
+ return "enabled".equals(tok[2]);
} else {
Slog.e(TAG, String.format("Unexpected response code %d", code));
return false;
diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
index 08d7ce6..c452590 100644
--- a/services/java/com/android/server/NativeDaemonConnector.java
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -128,12 +128,11 @@
Slog.e(TAG, String.format(
"Error handling '%s'", event), ex);
}
- } else {
- try {
- mResponseQueue.put(event);
- } catch (InterruptedException ex) {
- Slog.e(TAG, "Failed to put response onto queue", ex);
- }
+ }
+ try {
+ mResponseQueue.put(event);
+ } catch (InterruptedException ex) {
+ Slog.e(TAG, "Failed to put response onto queue", ex);
}
} catch (NumberFormatException nfe) {
Slog.w(TAG, String.format("Bad msg (%s)", event));
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index cbbc7be..c156150 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -35,6 +35,7 @@
import android.util.Log;
import android.util.Slog;
import java.util.ArrayList;
+import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import android.provider.Settings;
import android.content.ContentResolver;
@@ -226,44 +227,61 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult);
+ try {
+ return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Cannot communicate with native daemon to list interfaces");
+ }
}
public InterfaceConfiguration getInterfaceConfig(String iface) throws IllegalStateException {
- String rsp = mConnector.doCommand("interface getcfg " + iface).get(0);
+ String rsp;
+ try {
+ rsp = mConnector.doCommand("interface getcfg " + iface).get(0);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Cannot communicate with native daemon to get interface config");
+ }
Slog.d(TAG, String.format("rsp <%s>", rsp));
// Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz.zzz.zzz.zzz [flag1 flag2 flag3]
StringTokenizer st = new StringTokenizer(rsp);
+ InterfaceConfiguration cfg;
try {
- int code = Integer.parseInt(st.nextToken(" "));
- if (code != NetdResponseCode.InterfaceGetCfgResult) {
+ try {
+ int code = Integer.parseInt(st.nextToken(" "));
+ if (code != NetdResponseCode.InterfaceGetCfgResult) {
+ throw new IllegalStateException(
+ String.format("Expected code %d, but got %d",
+ NetdResponseCode.InterfaceGetCfgResult, code));
+ }
+ } catch (NumberFormatException nfe) {
throw new IllegalStateException(
- String.format("Expected code %d, but got %d",
- NetdResponseCode.InterfaceGetCfgResult, code));
+ String.format("Invalid response from daemon (%s)", rsp));
}
- } catch (NumberFormatException nfe) {
+
+ cfg = new InterfaceConfiguration();
+ cfg.hwAddr = st.nextToken(" ");
+ try {
+ cfg.ipAddr = stringToIpAddr(st.nextToken(" "));
+ } catch (UnknownHostException uhe) {
+ Slog.e(TAG, "Failed to parse ipaddr", uhe);
+ cfg.ipAddr = 0;
+ }
+
+ try {
+ cfg.netmask = stringToIpAddr(st.nextToken(" "));
+ } catch (UnknownHostException uhe) {
+ Slog.e(TAG, "Failed to parse netmask", uhe);
+ cfg.netmask = 0;
+ }
+ cfg.interfaceFlags = st.nextToken("]").trim() +"]";
+ } catch (NoSuchElementException nsee) {
throw new IllegalStateException(
String.format("Invalid response from daemon (%s)", rsp));
}
-
- InterfaceConfiguration cfg = new InterfaceConfiguration();
- cfg.hwAddr = st.nextToken(" ");
- try {
- cfg.ipAddr = stringToIpAddr(st.nextToken(" "));
- } catch (UnknownHostException uhe) {
- Slog.e(TAG, "Failed to parse ipaddr", uhe);
- cfg.ipAddr = 0;
- }
-
- try {
- cfg.netmask = stringToIpAddr(st.nextToken(" "));
- } catch (UnknownHostException uhe) {
- Slog.e(TAG, "Failed to parse netmask", uhe);
- cfg.netmask = 0;
- }
- cfg.interfaceFlags = st.nextToken("]").trim() +"]";
Slog.d(TAG, String.format("flags <%s>", cfg.interfaceFlags));
return cfg;
}
@@ -272,7 +290,12 @@
String iface, InterfaceConfiguration cfg) throws IllegalStateException {
String cmd = String.format("interface setcfg %s %s %s %s", iface,
intToIpString(cfg.ipAddr), intToIpString(cfg.netmask), cfg.interfaceFlags);
- mConnector.doCommand(cmd);
+ try {
+ mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate with native daemon to interface setcfg");
+ }
}
public void shutdown() {
@@ -289,20 +312,25 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- ArrayList<String> rsp = mConnector.doCommand("ipfwd status");
+ ArrayList<String> rsp;
+ try {
+ rsp = mConnector.doCommand("ipfwd status");
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate with native daemon to ipfwd status");
+ }
for (String line : rsp) {
- String []tok = line.split(" ");
+ String[] tok = line.split(" ");
+ if (tok.length < 3) {
+ Slog.e(TAG, "Malformed response from native daemon: " + line);
+ return false;
+ }
+
int code = Integer.parseInt(tok[0]);
if (code == NetdResponseCode.IpFwdStatusResult) {
// 211 Forwarding <enabled/disabled>
- if (tok.length !=2) {
- throw new IllegalStateException(
- String.format("Malformatted list entry '%s'", line));
- }
- if (tok[2].equals("enabled"))
- return true;
- return false;
+ return "enabled".equals(tok[2]);
} else {
throw new IllegalStateException(String.format("Unexpected response code %d", code));
}
@@ -326,29 +354,45 @@
for (String d : dhcpRange) {
cmd += " " + d;
}
- mConnector.doCommand(cmd);
+
+ try {
+ mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Unable to communicate to native daemon");
+ }
}
public void stopTethering() throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand("tether stop");
+ try {
+ mConnector.doCommand("tether stop");
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Unable to communicate to native daemon to stop tether");
+ }
}
public boolean isTetheringStarted() throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- ArrayList<String> rsp = mConnector.doCommand("tether status");
+ ArrayList<String> rsp;
+ try {
+ rsp = mConnector.doCommand("tether status");
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate to native daemon to get tether status");
+ }
for (String line : rsp) {
- String []tok = line.split(" ");
+ String[] tok = line.split(" ");
+ if (tok.length < 3) {
+ throw new IllegalStateException("Malformed response for tether status: " + line);
+ }
int code = Integer.parseInt(tok[0]);
if (code == NetdResponseCode.TetherStatusResult) {
// XXX: Tethering services <started/stopped> <TBD>...
- if (tok[2].equals("started"))
- return true;
- return false;
+ return "started".equals(tok[2]);
} else {
throw new IllegalStateException(String.format("Unexpected response code %d", code));
}
@@ -359,20 +403,35 @@
public void tetherInterface(String iface) throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand("tether interface add " + iface);
+ try {
+ mConnector.doCommand("tether interface add " + iface);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate to native daemon for adding tether interface");
+ }
}
public void untetherInterface(String iface) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand("tether interface remove " + iface);
+ try {
+ mConnector.doCommand("tether interface remove " + iface);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate to native daemon for removing tether interface");
+ }
}
public String[] listTetheredInterfaces() throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- return mConnector.doListCommand(
- "tether interface list", NetdResponseCode.TetherInterfaceListResult);
+ try {
+ return mConnector.doListCommand(
+ "tether interface list", NetdResponseCode.TetherInterfaceListResult);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate to native daemon for listing tether interfaces");
+ }
}
public void setDnsForwarders(String[] dns) throws IllegalStateException {
@@ -383,7 +442,12 @@
for (String s : dns) {
cmd += " " + InetAddress.getByName(s).getHostAddress();
}
- mConnector.doCommand(cmd);
+ try {
+ mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate to native daemon for setting tether dns");
+ }
} catch (UnknownHostException e) {
throw new IllegalStateException("Error resolving dns name", e);
}
@@ -392,30 +456,50 @@
public String[] getDnsForwarders() throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- return mConnector.doListCommand(
- "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult);
+ try {
+ return mConnector.doListCommand(
+ "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate to native daemon for listing tether dns");
+ }
}
public void enableNat(String internalInterface, String externalInterface)
throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand(
- String.format("nat enable %s %s", internalInterface, externalInterface));
+ try {
+ mConnector.doCommand(
+ String.format("nat enable %s %s", internalInterface, externalInterface));
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate to native daemon for enabling NAT interface");
+ }
}
public void disableNat(String internalInterface, String externalInterface)
throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand(
- String.format("nat disable %s %s", internalInterface, externalInterface));
+ try {
+ mConnector.doCommand(
+ String.format("nat disable %s %s", internalInterface, externalInterface));
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate to native daemon for disabling NAT interface");
+ }
}
public String[] listTtys() throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult);
+ try {
+ return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Unable to communicate to native daemon for listing TTYs");
+ }
}
public void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr,
@@ -430,31 +514,52 @@
InetAddress.getByName(dns2Addr).getHostAddress()));
} catch (UnknownHostException e) {
throw new IllegalStateException("Error resolving addr", e);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Error communicating to native daemon to attach pppd", e);
}
}
public void detachPppd(String tty) throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand(String.format("pppd detach %s", tty));
+ try {
+ mConnector.doCommand(String.format("pppd detach %s", tty));
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Error communicating to native daemon to detach pppd", e);
+ }
}
public void startUsbRNDIS() throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand("usb startrndis");
+ try {
+ mConnector.doCommand("usb startrndis");
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Error communicating to native daemon for starting RNDIS", e);
+ }
}
public void stopUsbRNDIS() throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand("usb stoprndis");
+ try {
+ mConnector.doCommand("usb stoprndis");
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Error communicating to native daemon", e);
+ }
}
public boolean isUsbRNDISStarted() throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- ArrayList<String> rsp = mConnector.doCommand("usb rndisstatus");
+ ArrayList<String> rsp;
+ try {
+ rsp = mConnector.doCommand("usb rndisstatus");
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException(
+ "Error communicating to native daemon to check RNDIS status", e);
+ }
for (String line : rsp) {
String []tok = line.split(" ");
@@ -476,31 +581,35 @@
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
- mConnector.doCommand(String.format("softap stop " + wlanIface));
- mConnector.doCommand(String.format("softap fwreload " + wlanIface + " AP"));
- mConnector.doCommand(String.format("softap start " + wlanIface));
- if (wifiConfig == null) {
- mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
- } else {
- /**
- * softap set arg1 arg2 arg3 [arg4 arg5 arg6 arg7 arg8]
- * argv1 - wlan interface
- * argv2 - softap interface
- * argv3 - SSID
- * argv4 - Security
- * argv5 - Key
- * argv6 - Channel
- * argv7 - Preamble
- * argv8 - Max SCB
- */
- String str = String.format("softap set " + wlanIface + " " + softapIface +
- " %s %s %s", convertQuotedString(wifiConfig.SSID),
- wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ?
- "wpa2-psk" : "open",
- convertQuotedString(wifiConfig.preSharedKey));
- mConnector.doCommand(str);
+ try {
+ mConnector.doCommand(String.format("softap stop " + wlanIface));
+ mConnector.doCommand(String.format("softap fwreload " + wlanIface + " AP"));
+ mConnector.doCommand(String.format("softap start " + wlanIface));
+ if (wifiConfig == null) {
+ mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
+ } else {
+ /**
+ * softap set arg1 arg2 arg3 [arg4 arg5 arg6 arg7 arg8]
+ * argv1 - wlan interface
+ * argv2 - softap interface
+ * argv3 - SSID
+ * argv4 - Security
+ * argv5 - Key
+ * argv6 - Channel
+ * argv7 - Preamble
+ * argv8 - Max SCB
+ */
+ String str = String.format("softap set " + wlanIface + " " + softapIface +
+ " %s %s %s", convertQuotedString(wifiConfig.SSID),
+ wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ?
+ "wpa2-psk" : "open",
+ convertQuotedString(wifiConfig.preSharedKey));
+ mConnector.doCommand(str);
+ }
+ mConnector.doCommand(String.format("softap startap"));
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Error communicating to native daemon to start softap", e);
}
- mConnector.doCommand(String.format("softap startap"));
}
private String convertQuotedString(String s) {
@@ -516,7 +625,12 @@
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
- mConnector.doCommand("softap stopap");
+ try {
+ mConnector.doCommand("softap stopap");
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Error communicating to native daemon to stop soft AP",
+ e);
+ }
}
public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface)
@@ -525,15 +639,19 @@
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
- if (wifiConfig == null) {
- mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
- } else {
- String str = String.format("softap set " + wlanIface + " " + softapIface +
- " %s %s %s", convertQuotedString(wifiConfig.SSID),
- wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ?
- "wpa2-psk" : "open",
- convertQuotedString(wifiConfig.preSharedKey));
- mConnector.doCommand(str);
+ try {
+ if (wifiConfig == null) {
+ mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
+ } else {
+ String str = String.format("softap set " + wlanIface + " " + softapIface
+ + " %s %s %s", convertQuotedString(wifiConfig.SSID),
+ wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ? "wpa2-psk" : "open",
+ convertQuotedString(wifiConfig.preSharedKey));
+ mConnector.doCommand(str);
+ }
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Error communicating to native daemon to set soft AP",
+ e);
}
}
@@ -541,9 +659,22 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
try {
- String rsp = mConnector.doCommand(
- String.format("interface read%scounter %s", (rx ? "rx" : "tx"), iface)).get(0);
- String []tok = rsp.split(" ");
+ String rsp;
+ try {
+ rsp = mConnector.doCommand(
+ String.format("interface read%scounter %s", (rx ? "rx" : "tx"), iface)).get(0);
+ } catch (NativeDaemonConnectorException e1) {
+ Slog.e(TAG, "Error communicating with native daemon", e1);
+ return -1;
+ }
+
+ String[] tok = rsp.split(" ");
+ if (tok.length < 2) {
+ Slog.e(TAG, String.format("Malformed response for reading %s interface",
+ (rx ? "rx" : "tx")));
+ return -1;
+ }
+
int code;
try {
code = Integer.parseInt(tok[0]);
@@ -575,17 +706,34 @@
public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand(String.format(
- "interface setthrottle %s %d %d", iface, rxKbps, txKbps));
+ try {
+ mConnector.doCommand(String.format(
+ "interface setthrottle %s %d %d", iface, rxKbps, txKbps));
+ } catch (NativeDaemonConnectorException e) {
+ Slog.e(TAG, "Error communicating with native daemon to set throttle", e);
+ }
}
private int getInterfaceThrottle(String iface, boolean rx) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
try {
- String rsp = mConnector.doCommand(
- String.format("interface getthrottle %s %s", iface,(rx ? "rx" : "tx"))).get(0);
- String []tok = rsp.split(" ");
+ String rsp;
+ try {
+ rsp = mConnector.doCommand(
+ String.format("interface getthrottle %s %s", iface,
+ (rx ? "rx" : "tx"))).get(0);
+ } catch (NativeDaemonConnectorException e) {
+ Slog.e(TAG, "Error communicating with native daemon to getthrottle", e);
+ return -1;
+ }
+
+ String[] tok = rsp.split(" ");
+ if (tok.length < 2) {
+ Slog.e(TAG, "Malformed response to getthrottle command");
+ return -1;
+ }
+
int code;
try {
code = Integer.parseInt(tok[0]);
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 0b84c8d..b70d69b 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -3623,21 +3623,19 @@
installedNativeLibraries = true;
+ // Always extract the shared library
String sharedLibraryFilePath = sharedLibraryDir.getPath() +
File.separator + libFileName;
File sharedLibraryFile = new File(sharedLibraryFilePath);
- if (! sharedLibraryFile.exists() ||
- sharedLibraryFile.length() != entry.getSize() ||
- sharedLibraryFile.lastModified() != entry.getTime()) {
- if (Config.LOGD) {
- Log.d(TAG, "Caching shared lib " + entry.getName());
- }
- if (mInstaller == null) {
- sharedLibraryDir.mkdir();
- }
- cacheNativeBinaryLI(pkg, zipFile, entry, sharedLibraryDir,
- sharedLibraryFile);
+
+ if (Config.LOGD) {
+ Log.d(TAG, "Caching shared lib " + entry.getName());
}
+ if (mInstaller == null) {
+ sharedLibraryDir.mkdir();
+ }
+ cacheNativeBinaryLI(pkg, zipFile, entry, sharedLibraryDir,
+ sharedLibraryFile);
}
if (!hasNativeLibraries)
return PACKAGE_INSTALL_NATIVE_NO_LIBRARIES;
@@ -3679,18 +3677,16 @@
String installGdbServerPath = installGdbServerDir.getPath() +
"/" + GDBSERVER;
File installGdbServerFile = new File(installGdbServerPath);
- if (! installGdbServerFile.exists() ||
- installGdbServerFile.length() != entry.getSize() ||
- installGdbServerFile.lastModified() != entry.getTime()) {
- if (Config.LOGD) {
- Log.d(TAG, "Caching gdbserver " + entry.getName());
- }
- if (mInstaller == null) {
- installGdbServerDir.mkdir();
- }
- cacheNativeBinaryLI(pkg, zipFile, entry, installGdbServerDir,
- installGdbServerFile);
+
+ if (Config.LOGD) {
+ Log.d(TAG, "Caching gdbserver " + entry.getName());
}
+ if (mInstaller == null) {
+ installGdbServerDir.mkdir();
+ }
+ cacheNativeBinaryLI(pkg, zipFile, entry, installGdbServerDir,
+ installGdbServerFile);
+
return PACKAGE_INSTALL_NATIVE_FOUND_LIBRARIES;
}
return PACKAGE_INSTALL_NATIVE_NO_LIBRARIES;
@@ -3704,6 +3700,16 @@
// one if ro.product.cpu.abi2 is defined.
//
private int cachePackageSharedLibsLI(PackageParser.Package pkg, File scanFile) {
+ // Remove all native binaries from a directory. This is used when upgrading
+ // a package: in case the new .apk doesn't contain a native binary that was
+ // in the old one (and thus installed), we need to remove it from
+ // /data/data/<appname>/lib
+ //
+ // The simplest way to do that is to remove all files in this directory,
+ // since it is owned by "system", applications are not supposed to write
+ // anything there.
+ removeNativeBinariesLI(pkg);
+
String cpuAbi = Build.CPU_ABI;
try {
int result = cachePackageSharedLibsForAbiLI(pkg, scanFile, cpuAbi);
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 7bbc32f..55f9d60 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -1029,11 +1029,10 @@
// If they gave a timeoutOverride it is the number of seconds
// to screen-off. Figure out where in the countdown cycle we
// should jump to.
- private void setTimeoutLocked(long now, long timeoutOverride, int nextState) {
+ private void setTimeoutLocked(long now, final long originalTimeoutOverride, int nextState) {
+ long timeoutOverride = originalTimeoutOverride;
if (mBootCompleted) {
synchronized (mLocks) {
- mHandler.removeCallbacks(mTimeoutTask);
- mTimeoutTask.nextState = nextState;
long when = 0;
if (timeoutOverride <= 0) {
switch (nextState)
@@ -1084,6 +1083,12 @@
+ " timeoutOverride=" + timeoutOverride
+ " nextState=" + nextState + " when=" + when);
}
+
+ mHandler.removeCallbacks(mTimeoutTask);
+ mTimeoutTask.nextState = nextState;
+ mTimeoutTask.remainingTimeoutOverride = timeoutOverride > 0
+ ? (originalTimeoutOverride - timeoutOverride)
+ : -1;
mHandler.postAtTime(mTimeoutTask, when);
mNextTimeout = when; // for debugging
}
@@ -1099,6 +1104,7 @@
private class TimeoutTask implements Runnable
{
int nextState; // access should be synchronized on mLocks
+ long remainingTimeoutOverride;
public void run()
{
synchronized (mLocks) {
@@ -1119,11 +1125,11 @@
{
case SCREEN_BRIGHT:
if (mDimDelay >= 0) {
- setTimeoutLocked(now, SCREEN_DIM);
+ setTimeoutLocked(now, remainingTimeoutOverride, SCREEN_DIM);
break;
}
case SCREEN_DIM:
- setTimeoutLocked(now, SCREEN_OFF);
+ setTimeoutLocked(now, remainingTimeoutOverride, SCREEN_OFF);
break;
}
}
@@ -2054,6 +2060,7 @@
+ " mUserState=0x" + Integer.toHexString(mUserState)
+ " mWakeLockState=0x" + Integer.toHexString(mWakeLockState)
+ " mProximitySensorActive=" + mProximitySensorActive
+ + " timeoutOverride=" + timeoutOverride
+ " force=" + force);
}
// ignore user activity if we are in the process of turning off the screen
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 4ee9560..278e8ca 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -177,7 +177,7 @@
/**
* Returns the unique device ID, for example, the IMEI for GSM and the MEID
- * for CDMA phones. Return null if device ID is not available.
+ * or ESN for CDMA phones. Return null if device ID is not available.
*
* <p>Requires Permission:
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index 1f5accf..58dfeb9 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -430,9 +430,14 @@
return mMeid;
}
- //returns MEID in CDMA
+ //returns MEID or ESN in CDMA
public String getDeviceId() {
- return getMeid();
+ String id = getMeid();
+ if ((id == null) || id.matches("^0*$")) {
+ Log.d(LOG_TAG, "getDeviceId(): MEID is not initialized use ESN");
+ id = getEsn();
+ }
+ return id;
}
public String getDeviceSvn() {