diff options
22 files changed, 875 insertions, 128 deletions
@@ -371,8 +371,6 @@ sample_dir := development/samples # (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.content.res.TypedArray; 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.Drawable; 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.Interpolator; 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 @@ public class ProgressBar extends View { } @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 @@ page.title=Location and Maps <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 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 more information, read the guide to <a +href="{@docRoot}guide/topics/location/obtaining-user-location.html">Obtaining User +Location</a>.</p> -<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 @@ Google APIs add-on, visit</p> <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 Binary files differnew file mode 100644 index 0000000..d58bfee --- /dev/null +++ b/docs/html/images/location/content-tagging.png diff --git a/docs/html/images/location/getting-location.png b/docs/html/images/location/getting-location.png Binary files differnew file mode 100644 index 0000000..a5905ec --- /dev/null +++ b/docs/html/images/location/getting-location.png diff --git a/docs/html/images/location/where-to-go.png b/docs/html/images/location/where-to-go.png Binary files differnew file mode 100644 index 0000000..59f5983 --- /dev/null +++ b/docs/html/images/location/where-to-go.png 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 @@ public final class Bitmap implements Parcelable { @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 @@ private: 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 @@ public: 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 @@ struct MediaWriter : public RefBase { 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 @@ LOCAL_SRC_FILES:= \ 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 @@ LOCAL_C_INCLUDES := \ $(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 @@ Visualizer::Visualizer (int32_t priority, 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 @@ status_t Visualizer::setCaptureSize(uint32_t size) } 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::getFft(uint8_t *fft) 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 @@ status_t StagefrightRecorder::getMaxAmplitude(int *max) { 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 @@ public: 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 @@ MPEG4Writer::~MPEG4Writer() { 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 @@ struct ARTSPController : public MediaExtractor { 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; + + mHandler->connect(msg); + + while (mState == CONNECTING) { + mCondition.wait(mLock); + } + + if (mState != CONNECTED) { + mHandler.clear(); + } - return OK; + 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 @@ sp<MetaData> ARTSPController::getTrackMetaData( 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 @@ struct MyHandler : public AHandler { 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 @@ struct MyHandler : public AHandler { 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 @@ struct MyHandler : public AHandler { case 'quit': { + if (mDoneMsg != NULL) { + mDoneMsg->setInt32("result", UNKNOWN_ERROR); + mDoneMsg->post(); + mDoneMsg = NULL; + } break; } @@ -380,6 +389,8 @@ private: }; 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 @@ public class WifiService extends IWifiManager.Stub { 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 @@ public class WifiService extends IWifiManager.Stub { 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 @@ public class WifiService extends IWifiManager.Stub { 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 @@ public class WifiService extends IWifiManager.Stub { 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.ArrayList; 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 @@ public class WifiStateTracker extends NetworkStateTracker { 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 @@ public class WifiStateTracker extends NetworkStateTracker { mTornDownByConnMgr = false; mLastBssid = null; mLastSsid = null; + mIsAnyNetworkDisabled.set(false); requestConnectionInfo(); SupplicantState supplState = mWifiInfo.getSupplicantState(); /** @@ -1585,6 +1590,10 @@ public class WifiStateTracker extends NetworkStateTracker { 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 @@ public class WifiStateTracker extends NetworkStateTracker { 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 @@ public class WifiStateTracker extends NetworkStateTracker { if (mWifiState.get() != WIFI_STATE_ENABLED) { return false; } + mIsAnyNetworkDisabled.set(true); return WifiNative.disableNetworkCommand(netId); } |