Merge "add new sensor types for handling gyro data and device orientation more efficiently." into gingerbread
diff --git a/Android.mk b/Android.mk
index 62c7679..c1324c9 100644
--- a/Android.mk
+++ b/Android.mk
@@ -371,8 +371,6 @@
# (see development/build/sdk.atree)
web_docs_sample_code_flags := \
-hdf android.hasSamples 1 \
- -samplecode $(sample_dir)/AccessibilityService \
- resources/samples/AccessibilityService "Accessibility Service" \
-samplecode $(sample_dir)/ApiDemos \
resources/samples/ApiDemos "API Demos" \
-samplecode $(sample_dir)/BackupRestore \
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index c0a546d..ec7d927 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -21,8 +21,9 @@
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
-import android.graphics.Shader;
import android.graphics.Rect;
+import android.graphics.Shader;
+import android.graphics.drawable.Animatable;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ClipDrawable;
@@ -30,11 +31,14 @@
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.StateListDrawable;
-import android.graphics.drawable.Animatable;
import android.graphics.drawable.shapes.RoundRectShape;
import android.graphics.drawable.shapes.Shape;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.Gravity;
+import android.view.RemotableViewMethod;
import android.view.View;
import android.view.ViewDebug;
import android.view.animation.AlphaAnimation;
@@ -44,9 +48,6 @@
import android.view.animation.LinearInterpolator;
import android.view.animation.Transformation;
import android.widget.RemoteViews.RemoteView;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.SystemClock;
import com.android.internal.R;
@@ -762,6 +763,7 @@
}
@Override
+ @RemotableViewMethod
public void setVisibility(int v) {
if (getVisibility() != v) {
super.setVisibility(v);
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/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/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/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 00860ae..7ca3741 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -1002,7 +1002,6 @@
@Override
protected void finalize() throws Throwable {
try {
- mRecycled = true;
nativeDestructor(mNativeBitmap);
} finally {
super.finalize();
diff --git a/include/media/Visualizer.h b/include/media/Visualizer.h
index 5d51de8..b8746c2 100644
--- a/include/media/Visualizer.h
+++ b/include/media/Visualizer.h
@@ -151,7 +151,6 @@
void *mCaptureCbkUser;
sp<CaptureThread> mCaptureThread;
uint32_t mCaptureFlags;
- void *mFftTable;
};
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index de82b38..2412f6a 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -40,6 +40,7 @@
virtual status_t stop();
virtual status_t pause();
virtual bool reachedEOS();
+ virtual status_t dump(int fd, const Vector<String16>& args);
void beginBox(const char *fourcc);
void writeInt8(int8_t x);
diff --git a/include/media/stagefright/MediaWriter.h b/include/media/stagefright/MediaWriter.h
index 151bf16..5cc8dcf 100644
--- a/include/media/stagefright/MediaWriter.h
+++ b/include/media/stagefright/MediaWriter.h
@@ -44,6 +44,10 @@
mListener = listener;
}
+ virtual status_t dump(int fd, const Vector<String16>& args) {
+ return OK;
+ }
+
protected:
virtual ~MediaWriter() {}
int64_t mMaxFileSizeLimitBytes;
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index 977e6be..b4d01f5 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -31,7 +31,8 @@
IEffect.cpp \
IEffectClient.cpp \
AudioEffect.cpp \
- Visualizer.cpp
+ Visualizer.cpp \
+ fixedfft.cpp.arm
LOCAL_SHARED_LIBRARIES := \
libui libcutils libutils libbinder libsonivox libicuuc libexpat libsurfaceflinger_client libcamera_client
@@ -50,11 +51,7 @@
$(JNI_H_INCLUDE) \
$(call include-path-for, graphics corecg) \
$(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
- external/speex/include \
- external/speex/libspeex \
external/icu4c/common \
external/expat/lib
-LOCAL_STATIC_LIBRARIES := libspeex
-
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libmedia/Visualizer.cpp b/media/libmedia/Visualizer.cpp
index 32cdb49..39552b6 100644
--- a/media/libmedia/Visualizer.cpp
+++ b/media/libmedia/Visualizer.cpp
@@ -26,10 +26,7 @@
#include <media/Visualizer.h>
-extern "C" {
-#define FLOATING_POINT 1
-#include "fftwrap.h"
-}
+extern void fixed_fft_real(int n, int32_t *v);
namespace android {
@@ -47,18 +44,10 @@
mCaptureCbkUser(NULL)
{
initCaptureSize();
- if (mCaptureSize != 0) {
- mFftTable = spx_fft_init(mCaptureSize);
- } else {
- mFftTable = NULL;
- }
}
Visualizer::~Visualizer()
{
- if (mFftTable != NULL) {
- spx_fft_destroy(mFftTable);
- }
}
status_t Visualizer::setEnabled(bool enabled)
@@ -163,11 +152,6 @@
}
if (status == NO_ERROR) {
mCaptureSize = size;
- if (mFftTable != NULL) {
- spx_fft_destroy(mFftTable);
- }
- mFftTable = spx_fft_init(mCaptureSize);
- LOGV("setCaptureSize size %d mFftTable %p", mCaptureSize, mFftTable);
}
return status;
@@ -219,19 +203,24 @@
status_t Visualizer::doFft(uint8_t *fft, uint8_t *waveform)
{
- if (mFftTable == NULL) {
- return NO_INIT;
+ int32_t workspace[mCaptureSize >> 1];
+ int32_t nonzero = 0;
+
+ for (uint32_t i = 0; i < mCaptureSize; i += 2) {
+ workspace[i >> 1] = (waveform[i] ^ 0x80) << 23;
+ workspace[i >> 1] |= (waveform[i + 1] ^ 0x80) << 7;
+ nonzero |= workspace[i >> 1];
}
- float fsrc[mCaptureSize];
- for (uint32_t i = 0; i < mCaptureSize; i++) {
- fsrc[i] = (int16_t)(waveform[i] ^ 0x80) << 8;
+ if (nonzero) {
+ fixed_fft_real(mCaptureSize >> 1, workspace);
}
- float fdst[mCaptureSize];
- spx_fft_float(mFftTable, fsrc, fdst);
- for (uint32_t i = 0; i < mCaptureSize; i++) {
- fft[i] = (uint8_t)((int32_t)fdst[i] >> 8);
+
+ for (uint32_t i = 0; i < mCaptureSize; i += 2) {
+ fft[i] = workspace[i >> 1] >> 23;
+ fft[i + 1] = workspace[i >> 1] >> 7;
}
+
return NO_ERROR;
}
diff --git a/media/libmedia/fixedfft.cpp b/media/libmedia/fixedfft.cpp
new file mode 100644
index 0000000..28eb05a
--- /dev/null
+++ b/media/libmedia/fixedfft.cpp
@@ -0,0 +1,160 @@
+/*
+ * 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.
+ */
+
+/* A Fixed point implementation of Fast Fourier Transform (FFT). Complex numbers
+ * are represented by 32-bit integers, where higher 16 bits are real part and
+ * lower ones are imaginary part. Few compromises are made between efficiency,
+ * accuracy, and maintainability. To make it fast, arithmetic shifts are used
+ * instead of divisions, and bitwise inverses are used instead of negates. To
+ * keep it small, only radix-2 Cooley-Tukey algorithm is implemented, and only
+ * half of the twiddle factors are stored. Although there are still ways to make
+ * it even faster or smaller, it costs too much on one of the aspects.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <machine/cpu-features.h>
+
+#define LOG_FFT_SIZE 10
+#define MAX_FFT_SIZE (1 << LOG_FFT_SIZE)
+
+static const int32_t twiddle[MAX_FFT_SIZE / 4] = {
+ 0x00008000, 0xff378001, 0xfe6e8002, 0xfda58006, 0xfcdc800a, 0xfc13800f,
+ 0xfb4a8016, 0xfa81801e, 0xf9b88027, 0xf8ef8032, 0xf827803e, 0xf75e804b,
+ 0xf6958059, 0xf5cd8068, 0xf5058079, 0xf43c808b, 0xf374809e, 0xf2ac80b2,
+ 0xf1e480c8, 0xf11c80de, 0xf05580f6, 0xef8d8110, 0xeec6812a, 0xedff8146,
+ 0xed388163, 0xec718181, 0xebab81a0, 0xeae481c1, 0xea1e81e2, 0xe9588205,
+ 0xe892822a, 0xe7cd824f, 0xe7078276, 0xe642829d, 0xe57d82c6, 0xe4b982f1,
+ 0xe3f4831c, 0xe3308349, 0xe26d8377, 0xe1a983a6, 0xe0e683d6, 0xe0238407,
+ 0xdf61843a, 0xde9e846e, 0xdddc84a3, 0xdd1b84d9, 0xdc598511, 0xdb998549,
+ 0xdad88583, 0xda1885be, 0xd95885fa, 0xd8988637, 0xd7d98676, 0xd71b86b6,
+ 0xd65c86f6, 0xd59e8738, 0xd4e1877b, 0xd42487c0, 0xd3678805, 0xd2ab884c,
+ 0xd1ef8894, 0xd13488dd, 0xd0798927, 0xcfbe8972, 0xcf0489be, 0xce4b8a0c,
+ 0xcd928a5a, 0xccd98aaa, 0xcc218afb, 0xcb698b4d, 0xcab28ba0, 0xc9fc8bf5,
+ 0xc9468c4a, 0xc8908ca1, 0xc7db8cf8, 0xc7278d51, 0xc6738dab, 0xc5c08e06,
+ 0xc50d8e62, 0xc45b8ebf, 0xc3a98f1d, 0xc2f88f7d, 0xc2488fdd, 0xc198903e,
+ 0xc0e990a1, 0xc03a9105, 0xbf8c9169, 0xbedf91cf, 0xbe329236, 0xbd86929e,
+ 0xbcda9307, 0xbc2f9371, 0xbb8593dc, 0xbadc9448, 0xba3394b5, 0xb98b9523,
+ 0xb8e39592, 0xb83c9603, 0xb7969674, 0xb6f196e6, 0xb64c9759, 0xb5a897ce,
+ 0xb5059843, 0xb46298b9, 0xb3c09930, 0xb31f99a9, 0xb27f9a22, 0xb1df9a9c,
+ 0xb1409b17, 0xb0a29b94, 0xb0059c11, 0xaf689c8f, 0xaecc9d0e, 0xae319d8e,
+ 0xad979e0f, 0xacfd9e91, 0xac659f14, 0xabcd9f98, 0xab36a01c, 0xaaa0a0a2,
+ 0xaa0aa129, 0xa976a1b0, 0xa8e2a238, 0xa84fa2c2, 0xa7bda34c, 0xa72ca3d7,
+ 0xa69ca463, 0xa60ca4f0, 0xa57ea57e, 0xa4f0a60c, 0xa463a69c, 0xa3d7a72c,
+ 0xa34ca7bd, 0xa2c2a84f, 0xa238a8e2, 0xa1b0a976, 0xa129aa0a, 0xa0a2aaa0,
+ 0xa01cab36, 0x9f98abcd, 0x9f14ac65, 0x9e91acfd, 0x9e0fad97, 0x9d8eae31,
+ 0x9d0eaecc, 0x9c8faf68, 0x9c11b005, 0x9b94b0a2, 0x9b17b140, 0x9a9cb1df,
+ 0x9a22b27f, 0x99a9b31f, 0x9930b3c0, 0x98b9b462, 0x9843b505, 0x97ceb5a8,
+ 0x9759b64c, 0x96e6b6f1, 0x9674b796, 0x9603b83c, 0x9592b8e3, 0x9523b98b,
+ 0x94b5ba33, 0x9448badc, 0x93dcbb85, 0x9371bc2f, 0x9307bcda, 0x929ebd86,
+ 0x9236be32, 0x91cfbedf, 0x9169bf8c, 0x9105c03a, 0x90a1c0e9, 0x903ec198,
+ 0x8fddc248, 0x8f7dc2f8, 0x8f1dc3a9, 0x8ebfc45b, 0x8e62c50d, 0x8e06c5c0,
+ 0x8dabc673, 0x8d51c727, 0x8cf8c7db, 0x8ca1c890, 0x8c4ac946, 0x8bf5c9fc,
+ 0x8ba0cab2, 0x8b4dcb69, 0x8afbcc21, 0x8aaaccd9, 0x8a5acd92, 0x8a0cce4b,
+ 0x89becf04, 0x8972cfbe, 0x8927d079, 0x88ddd134, 0x8894d1ef, 0x884cd2ab,
+ 0x8805d367, 0x87c0d424, 0x877bd4e1, 0x8738d59e, 0x86f6d65c, 0x86b6d71b,
+ 0x8676d7d9, 0x8637d898, 0x85fad958, 0x85beda18, 0x8583dad8, 0x8549db99,
+ 0x8511dc59, 0x84d9dd1b, 0x84a3dddc, 0x846ede9e, 0x843adf61, 0x8407e023,
+ 0x83d6e0e6, 0x83a6e1a9, 0x8377e26d, 0x8349e330, 0x831ce3f4, 0x82f1e4b9,
+ 0x82c6e57d, 0x829de642, 0x8276e707, 0x824fe7cd, 0x822ae892, 0x8205e958,
+ 0x81e2ea1e, 0x81c1eae4, 0x81a0ebab, 0x8181ec71, 0x8163ed38, 0x8146edff,
+ 0x812aeec6, 0x8110ef8d, 0x80f6f055, 0x80def11c, 0x80c8f1e4, 0x80b2f2ac,
+ 0x809ef374, 0x808bf43c, 0x8079f505, 0x8068f5cd, 0x8059f695, 0x804bf75e,
+ 0x803ef827, 0x8032f8ef, 0x8027f9b8, 0x801efa81, 0x8016fb4a, 0x800ffc13,
+ 0x800afcdc, 0x8006fda5, 0x8002fe6e, 0x8001ff37,
+};
+
+/* Returns the multiplication of \conj{a} and {b}. */
+static inline int32_t mult(int32_t a, int32_t b)
+{
+#if __ARM_ARCH__ >= 6
+ int32_t t = b;
+ __asm__("smuad %0, %0, %1" : "+r" (t) : "r" (a));
+ __asm__("smusdx %0, %0, %1" : "+r" (b) : "r" (a));
+ __asm__("pkhtb %0, %0, %1, ASR #16" : "+r" (t) : "r" (b));
+ return t;
+#else
+ return (((a >> 16) * (b >> 16) + (int16_t)a * (int16_t)b) & ~0xFFFF) |
+ ((((a >> 16) * (int16_t)b - (int16_t)a * (b >> 16)) >> 16) & 0xFFFF);
+#endif
+}
+
+static inline int32_t half(int32_t a)
+{
+#if __ARM_ARCH__ >= 6
+ __asm__("shadd16 %0, %0, %1" : "+r" (a) : "r" (0));
+ return a;
+#else
+ return ((a >> 1) & ~0x8000) | (a & 0x8000);
+#endif
+}
+
+void fixed_fft(int n, int32_t *v)
+{
+ int scale = LOG_FFT_SIZE, i, p, r;
+
+ for (r = 0, i = 1; i < n; ++i) {
+ for (p = n; !(p & r); p >>= 1, r ^= p);
+ if (i < r) {
+ int32_t t = v[i];
+ v[i] = v[r];
+ v[r] = t;
+ }
+ }
+
+ for (p = 1; p < n; p <<= 1) {
+ --scale;
+
+ for (i = 0; i < n; i += p << 1) {
+ int32_t x = half(v[i]);
+ int32_t y = half(v[i + p]);
+ v[i] = x + y;
+ v[i + p] = x - y;
+ }
+
+ for (r = 1; r < p; ++r) {
+ int32_t w = MAX_FFT_SIZE / 4 - (r << scale);
+ i = w >> 31;
+ w = twiddle[(w ^ i) - i] ^ (i << 16);
+ for (i = r; i < n; i += p << 1) {
+ int32_t x = half(v[i]);
+ int32_t y = mult(w, v[i + p]);
+ v[i] = x - y;
+ v[i + p] = x + y;
+ }
+ }
+ }
+}
+
+void fixed_fft_real(int n, int32_t *v)
+{
+ int scale = LOG_FFT_SIZE, m = n >> 1, i;
+
+ fixed_fft(n, v);
+ for (i = 1; i <= n; i <<= 1, --scale);
+ v[0] = mult(~v[0], 0x80008000);
+ v[m] = half(v[m]);
+
+ for (i = 1; i < n >> 1; ++i) {
+ int32_t x = half(v[i]);
+ int32_t z = half(v[n - i]);
+ int32_t y = z - (x ^ 0xFFFF);
+ x = half(x + (z ^ 0xFFFF));
+ y = mult(y, twiddle[i << scale]);
+ v[i] = x - y;
+ v[n - i] = (x + y) ^ 0xFFFF;
+ }
+}
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 796731b..cf01ff6 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -1162,11 +1162,19 @@
return OK;
}
-status_t StagefrightRecorder::dump(int fd, const Vector<String16>& args) const {
+status_t StagefrightRecorder::dump(
+ int fd, const Vector<String16>& args) const {
+ LOGV("dump");
const size_t SIZE = 256;
char buffer[SIZE];
String8 result;
- snprintf(buffer, SIZE, " Recorder: %p", this);
+ if (mWriter != 0) {
+ mWriter->dump(fd, args);
+ } else {
+ snprintf(buffer, SIZE, " No file writer\n");
+ result.append(buffer);
+ }
+ snprintf(buffer, SIZE, " Recorder: %p\n", this);
snprintf(buffer, SIZE, " Output file (fd %d):\n", mOutputFd);
result.append(buffer);
snprintf(buffer, SIZE, " File format: %d\n", mOutputFormat);
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 568037e..e36d9fe 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -60,6 +60,7 @@
bool isAudio() const { return mIsAudio; }
bool isMPEG4() const { return mIsMPEG4; }
void addChunkOffset(off_t offset) { mChunkOffsets.push_back(offset); }
+ status_t dump(int fd, const Vector<String16>& args) const;
private:
MPEG4Writer *mOwner;
@@ -217,6 +218,37 @@
mTracks.clear();
}
+status_t MPEG4Writer::dump(
+ int fd, const Vector<String16>& args) {
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+ snprintf(buffer, SIZE, " MPEG4Writer %p\n", this);
+ result.append(buffer);
+ snprintf(buffer, SIZE, " mStarted: %s\n", mStarted? "true": "false");
+ result.append(buffer);
+ ::write(fd, result.string(), result.size());
+ for (List<Track *>::iterator it = mTracks.begin();
+ it != mTracks.end(); ++it) {
+ (*it)->dump(fd, args);
+ }
+ return OK;
+}
+
+status_t MPEG4Writer::Track::dump(
+ int fd, const Vector<String16>& args) const {
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+ snprintf(buffer, SIZE, " %s track\n", mIsAudio? "Audio": "Video");
+ result.append(buffer);
+ snprintf(buffer, SIZE, " reached EOS: %s\n",
+ mReachedEOS? "true": "false");
+ result.append(buffer);
+ ::write(fd, result.string(), result.size());
+ return OK;
+}
+
status_t MPEG4Writer::addSource(const sp<MediaSource> &source) {
Track *track = new Track(this, source);
mTracks.push_back(track);
diff --git a/media/libstagefright/include/ARTSPController.h b/media/libstagefright/include/ARTSPController.h
index 55efd41..2542e4e 100644
--- a/media/libstagefright/include/ARTSPController.h
+++ b/media/libstagefright/include/ARTSPController.h
@@ -19,6 +19,7 @@
#define A_RTSP_CONTROLLER_H_
#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AHandlerReflector.h>
#include <media/stagefright/MediaExtractor.h>
namespace android {
@@ -38,12 +39,32 @@
virtual sp<MetaData> getTrackMetaData(
size_t index, uint32_t flags);
+ void onMessageReceived(const sp<AMessage> &msg);
+
protected:
virtual ~ARTSPController();
private:
+ enum {
+ kWhatConnectDone = 'cdon',
+ kWhatDisconnectDone = 'ddon',
+ };
+
+ enum State {
+ DISCONNECTED,
+ CONNECTED,
+ CONNECTING,
+ };
+
+ Mutex mLock;
+ Condition mCondition;
+
+ State mState;
+ status_t mConnectionResult;
+
sp<ALooper> mLooper;
sp<MyHandler> mHandler;
+ sp<AHandlerReflector<ARTSPController> > mReflector;
DISALLOW_EVIL_CONSTRUCTORS(ARTSPController);
};
diff --git a/media/libstagefright/rtsp/ARTSPController.cpp b/media/libstagefright/rtsp/ARTSPController.cpp
index 195323e..ceae3a6 100644
--- a/media/libstagefright/rtsp/ARTSPController.cpp
+++ b/media/libstagefright/rtsp/ARTSPController.cpp
@@ -26,31 +26,57 @@
namespace android {
ARTSPController::ARTSPController(const sp<ALooper> &looper)
- : mLooper(looper) {
+ : mState(DISCONNECTED),
+ mLooper(looper) {
+ mReflector = new AHandlerReflector<ARTSPController>(this);
+ looper->registerHandler(mReflector);
}
ARTSPController::~ARTSPController() {
+ disconnect();
+ mLooper->unregisterHandler(mReflector->id());
}
status_t ARTSPController::connect(const char *url) {
- if (mHandler != NULL) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mState != DISCONNECTED) {
return ERROR_ALREADY_CONNECTED;
}
+ sp<AMessage> msg = new AMessage(kWhatConnectDone, mReflector->id());
+
mHandler = new MyHandler(url, mLooper);
- mHandler->connect();
- sleep(10);
+ mState = CONNECTING;
- return OK;
+ mHandler->connect(msg);
+
+ while (mState == CONNECTING) {
+ mCondition.wait(mLock);
+ }
+
+ if (mState != CONNECTED) {
+ mHandler.clear();
+ }
+
+ return mConnectionResult;
}
void ARTSPController::disconnect() {
- if (mHandler == NULL) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mState != CONNECTED) {
return;
}
- mHandler->disconnect();
+ sp<AMessage> msg = new AMessage(kWhatDisconnectDone, mReflector->id());
+ mHandler->disconnect(msg);
+
+ while (mState == CONNECTED) {
+ mCondition.wait(mLock);
+ }
+
mHandler.clear();
}
@@ -75,4 +101,31 @@
return mHandler->getPacketSource(index)->getFormat();
}
+void ARTSPController::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatConnectDone:
+ {
+ Mutex::Autolock autoLock(mLock);
+
+ CHECK(msg->findInt32("result", &mConnectionResult));
+ mState = (mConnectionResult == OK) ? CONNECTED : DISCONNECTED;
+
+ mCondition.signal();
+ break;
+ }
+
+ case kWhatDisconnectDone:
+ {
+ Mutex::Autolock autoLock(mLock);
+ mState = DISCONNECTED;
+ mCondition.signal();
+ break;
+ }
+
+ default:
+ TRESPASS();
+ break;
+ }
+}
+
} // namespace android
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index b19ad48..90070c9 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -45,18 +45,21 @@
PRIORITY_HIGHEST);
}
- void connect() {
+ void connect(const sp<AMessage> &doneMsg) {
+ mDoneMsg = doneMsg;
+
mLooper->registerHandler(this);
mLooper->registerHandler(mConn);
(1 ? mNetLooper : mLooper)->registerHandler(mRTPConn);
- sp<AMessage> reply = new AMessage('conn', id());
+ sp<AMessage> reply = new AMessage('conn', id());
mConn->connect(mSessionURL.c_str(), reply);
}
- void disconnect() {
- sp<AMessage> reply = new AMessage('disc', id());
- mConn->disconnect(reply);
+ void disconnect(const sp<AMessage> &doneMsg) {
+ mDoneMsg = doneMsg;
+
+ (new AMessage('abor', id()))->post();
}
virtual void onMessageReceived(const sp<AMessage> &msg) {
@@ -250,8 +253,9 @@
CHECK_EQ(response->mStatusCode, 200u);
- sp<AMessage> msg = new AMessage('abor', id());
- msg->post(60000000ll);
+ mDoneMsg->setInt32("result", OK);
+ mDoneMsg->post();
+ mDoneMsg = NULL;
} else {
sp<AMessage> reply = new AMessage('disc', id());
mConn->disconnect(reply);
@@ -301,6 +305,11 @@
case 'quit':
{
+ if (mDoneMsg != NULL) {
+ mDoneMsg->setInt32("result", UNKNOWN_ERROR);
+ mDoneMsg->post();
+ mDoneMsg = NULL;
+ }
break;
}
@@ -380,6 +389,8 @@
};
Vector<TrackInfo> mTracks;
+ sp<AMessage> mDoneMsg;
+
void setupTrack(size_t index) {
sp<APacketSource> source =
new APacketSource(mSessionDesc, index);
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index af4d7e4..0eca082 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -171,6 +171,7 @@
private static final int MESSAGE_START_ACCESS_POINT = 6;
private static final int MESSAGE_STOP_ACCESS_POINT = 7;
private static final int MESSAGE_SET_CHANNELS = 8;
+ private static final int MESSAGE_ENABLE_NETWORKS = 9;
private final WifiHandler mWifiHandler;
@@ -1663,6 +1664,12 @@
mDeviceIdle = false;
mScreenOff = false;
mWifiStateTracker.enableRssiPolling(true);
+ /* DHCP or other temporary failures in the past can prevent
+ * a disabled network from being connected to, enable on screen on
+ */
+ if (mWifiStateTracker.isAnyNetworkDisabled()) {
+ sendEnableNetworksMessage();
+ }
} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
Slog.d(TAG, "ACTION_SCREEN_OFF");
mScreenOff = true;
@@ -1794,6 +1801,10 @@
uid, 0, wifiConfig).sendToTarget();
}
+ private void sendEnableNetworksMessage() {
+ Message.obtain(mWifiHandler, MESSAGE_ENABLE_NETWORKS).sendToTarget();
+ }
+
private void updateWifiState() {
// send a message so it's all serialized
Message.obtain(mWifiHandler, MESSAGE_UPDATE_STATE, 0, 0).sendToTarget();
@@ -1956,6 +1967,10 @@
setNumAllowedChannelsBlocking(msg.arg1, msg.arg2 == 1);
break;
+ case MESSAGE_ENABLE_NETWORKS:
+ mWifiStateTracker.enableAllNetworks(getConfiguredNetworks());
+ break;
+
}
}
}
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 0cc1f46..22dbda3 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -59,6 +59,7 @@
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* Track the state of Wifi connectivity. All event handling is done here,
@@ -216,6 +217,9 @@
private boolean mUseStaticIp = false;
private int mReconnectCount;
+ /* Tracks if any network in the configuration is disabled */
+ private AtomicBoolean mIsAnyNetworkDisabled = new AtomicBoolean(false);
+
// used to store the (non-persisted) num determined during device boot
// (from mcc or other phone info) before the driver is started.
private int mNumAllowedChannels = 0;
@@ -814,6 +818,7 @@
mTornDownByConnMgr = false;
mLastBssid = null;
mLastSsid = null;
+ mIsAnyNetworkDisabled.set(false);
requestConnectionInfo();
SupplicantState supplState = mWifiInfo.getSupplicantState();
/**
@@ -1585,6 +1590,10 @@
mWifiState.set(wifiState);
}
+ public boolean isAnyNetworkDisabled() {
+ return mIsAnyNetworkDisabled.get();
+ }
+
/**
* The WifiNative interface functions are listed below.
* The only native call that is not synchronized on
@@ -1785,10 +1794,28 @@
if (mWifiState.get() != WIFI_STATE_ENABLED) {
return false;
}
+ if (disableOthers) mIsAnyNetworkDisabled.set(true);
return WifiNative.enableNetworkCommand(netId, disableOthers);
}
/**
+ * Enable all networks
+ *
+ * @param networks list of configured networks
+ */
+ public synchronized void enableAllNetworks(List<WifiConfiguration> networks) {
+ if (mWifiState.get() != WIFI_STATE_ENABLED) {
+ return;
+ }
+ mIsAnyNetworkDisabled.set(false);
+ for (WifiConfiguration config : networks) {
+ if (config.status == WifiConfiguration.Status.DISABLED) {
+ WifiNative.enableNetworkCommand(config.networkId, false);
+ }
+ }
+ }
+
+ /**
* Disable a network
*
* @param netId network id of the network
@@ -1798,6 +1825,7 @@
if (mWifiState.get() != WIFI_STATE_ENABLED) {
return false;
}
+ mIsAnyNetworkDisabled.set(true);
return WifiNative.disableNetworkCommand(netId);
}