Location.java revision 6fa9ad4afcd762aea519ff61811386c23d18ddb2
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.location;
18
19import android.os.Bundle;
20import android.os.Parcel;
21import android.os.Parcelable;
22import android.os.SystemClock;
23import android.util.Printer;
24import android.util.TimeUtils;
25
26import java.text.DecimalFormat;
27import java.util.StringTokenizer;
28
29/**
30 * A class representing a geographic location sensed at a particular
31 * time (a "fix").  A location consists of a latitude and longitude, a
32 * UTC timestamp. and optionally information on altitude, speed, and
33 * bearing.
34 *
35 * <p> Information specific to a particular provider or class of
36 * providers may be communicated to the application using getExtras,
37 * which returns a Bundle of key/value pairs.  Each provider will only
38 * provide those entries for which information is available.
39 */
40public class Location implements Parcelable {
41    /**
42     * Constant used to specify formatting of a latitude or longitude
43     * in the form "[+-]DDD.DDDDD where D indicates degrees.
44     */
45    public static final int FORMAT_DEGREES = 0;
46
47    /**
48     * Constant used to specify formatting of a latitude or longitude
49     * in the form "[+-]DDD:MM.MMMMM" where D indicates degrees and
50     * M indicates minutes of arc (1 minute = 1/60th of a degree).
51     */
52    public static final int FORMAT_MINUTES = 1;
53
54    /**
55     * Constant used to specify formatting of a latitude or longitude
56     * in the form "DDD:MM:SS.SSSSS" where D indicates degrees, M
57     * indicates minutes of arc, and S indicates seconds of arc (1
58     * minute = 1/60th of a degree, 1 second = 1/3600th of a degree).
59     */
60    public static final int FORMAT_SECONDS = 2;
61
62    private String mProvider;
63    private long mTime = 0;
64    private long mElapsedRealtimeNano = 0;
65    private double mLatitude = 0.0;
66    private double mLongitude = 0.0;
67    private boolean mHasAltitude = false;
68    private double mAltitude = 0.0f;
69    private boolean mHasSpeed = false;
70    private float mSpeed = 0.0f;
71    private boolean mHasBearing = false;
72    private float mBearing = 0.0f;
73    private boolean mHasAccuracy = false;
74    private float mAccuracy = 0.0f;
75    private Bundle mExtras = null;
76
77    // Cache the inputs and outputs of computeDistanceAndBearing
78    // so calls to distanceTo() and bearingTo() can share work
79    private double mLat1 = 0.0;
80    private double mLon1 = 0.0;
81    private double mLat2 = 0.0;
82    private double mLon2 = 0.0;
83    private float mDistance = 0.0f;
84    private float mInitialBearing = 0.0f;
85    // Scratchpad
86    private float[] mResults = new float[2];
87
88    /**
89     * Constructs a new Location.  By default, time, latitude,
90     * longitude, and numSatellites are 0; hasAltitude, hasSpeed, and
91     * hasBearing are false; and there is no extra information.
92     *
93     * @param provider the name of the location provider that generated this
94     * location fix.
95     */
96    public Location(String provider) {
97        mProvider = provider;
98    }
99
100    /**
101     * Constructs a new Location object that is a copy of the given
102     * location.
103     */
104    public Location(Location l) {
105        set(l);
106    }
107
108    /**
109     * Sets the contents of the location to the values from the given location.
110     */
111    public void set(Location l) {
112        mProvider = l.mProvider;
113        mTime = l.mTime;
114        mElapsedRealtimeNano = l.mElapsedRealtimeNano;
115        mLatitude = l.mLatitude;
116        mLongitude = l.mLongitude;
117        mHasAltitude = l.mHasAltitude;
118        mAltitude = l.mAltitude;
119        mHasSpeed = l.mHasSpeed;
120        mSpeed = l.mSpeed;
121        mHasBearing = l.mHasBearing;
122        mBearing = l.mBearing;
123        mHasAccuracy = l.mHasAccuracy;
124        mAccuracy = l.mAccuracy;
125        mExtras = (l.mExtras == null) ? null : new Bundle(l.mExtras);
126    }
127
128    /**
129     * Clears the contents of the location.
130     */
131    public void reset() {
132        mProvider = null;
133        mTime = 0;
134        mElapsedRealtimeNano = 0;
135        mLatitude = 0;
136        mLongitude = 0;
137        mHasAltitude = false;
138        mAltitude = 0;
139        mHasSpeed = false;
140        mSpeed = 0;
141        mHasBearing = false;
142        mBearing = 0;
143        mHasAccuracy = false;
144        mAccuracy = 0;
145        mExtras = null;
146    }
147
148    /**
149     * Converts a coordinate to a String representation. The outputType
150     * may be one of FORMAT_DEGREES, FORMAT_MINUTES, or FORMAT_SECONDS.
151     * The coordinate must be a valid double between -180.0 and 180.0.
152     *
153     * @throws IllegalArgumentException if coordinate is less than
154     * -180.0, greater than 180.0, or is not a number.
155     * @throws IllegalArgumentException if outputType is not one of
156     * FORMAT_DEGREES, FORMAT_MINUTES, or FORMAT_SECONDS.
157     */
158    public static String convert(double coordinate, int outputType) {
159        if (coordinate < -180.0 || coordinate > 180.0 ||
160            Double.isNaN(coordinate)) {
161            throw new IllegalArgumentException("coordinate=" + coordinate);
162        }
163        if ((outputType != FORMAT_DEGREES) &&
164            (outputType != FORMAT_MINUTES) &&
165            (outputType != FORMAT_SECONDS)) {
166            throw new IllegalArgumentException("outputType=" + outputType);
167        }
168
169        StringBuilder sb = new StringBuilder();
170
171        // Handle negative values
172        if (coordinate < 0) {
173            sb.append('-');
174            coordinate = -coordinate;
175        }
176
177        DecimalFormat df = new DecimalFormat("###.#####");
178        if (outputType == FORMAT_MINUTES || outputType == FORMAT_SECONDS) {
179            int degrees = (int) Math.floor(coordinate);
180            sb.append(degrees);
181            sb.append(':');
182            coordinate -= degrees;
183            coordinate *= 60.0;
184            if (outputType == FORMAT_SECONDS) {
185                int minutes = (int) Math.floor(coordinate);
186                sb.append(minutes);
187                sb.append(':');
188                coordinate -= minutes;
189                coordinate *= 60.0;
190            }
191        }
192        sb.append(df.format(coordinate));
193        return sb.toString();
194    }
195
196    /**
197     * Converts a String in one of the formats described by
198     * FORMAT_DEGREES, FORMAT_MINUTES, or FORMAT_SECONDS into a
199     * double.
200     *
201     * @throws NullPointerException if coordinate is null
202     * @throws IllegalArgumentException if the coordinate is not
203     * in one of the valid formats.
204     */
205    public static double convert(String coordinate) {
206        // IllegalArgumentException if bad syntax
207        if (coordinate == null) {
208            throw new NullPointerException("coordinate");
209        }
210
211        boolean negative = false;
212        if (coordinate.charAt(0) == '-') {
213            coordinate = coordinate.substring(1);
214            negative = true;
215        }
216
217        StringTokenizer st = new StringTokenizer(coordinate, ":");
218        int tokens = st.countTokens();
219        if (tokens < 1) {
220            throw new IllegalArgumentException("coordinate=" + coordinate);
221        }
222        try {
223            String degrees = st.nextToken();
224            double val;
225            if (tokens == 1) {
226                val = Double.parseDouble(degrees);
227                return negative ? -val : val;
228            }
229
230            String minutes = st.nextToken();
231            int deg = Integer.parseInt(degrees);
232            double min;
233            double sec = 0.0;
234
235            if (st.hasMoreTokens()) {
236                min = Integer.parseInt(minutes);
237                String seconds = st.nextToken();
238                sec = Double.parseDouble(seconds);
239            } else {
240                min = Double.parseDouble(minutes);
241            }
242
243            boolean isNegative180 = negative && (deg == 180) &&
244                (min == 0) && (sec == 0);
245
246            // deg must be in [0, 179] except for the case of -180 degrees
247            if ((deg < 0.0) || (deg > 179 && !isNegative180)) {
248                throw new IllegalArgumentException("coordinate=" + coordinate);
249            }
250            if (min < 0 || min > 59) {
251                throw new IllegalArgumentException("coordinate=" +
252                        coordinate);
253            }
254            if (sec < 0 || sec > 59) {
255                throw new IllegalArgumentException("coordinate=" +
256                        coordinate);
257            }
258
259            val = deg*3600.0 + min*60.0 + sec;
260            val /= 3600.0;
261            return negative ? -val : val;
262        } catch (NumberFormatException nfe) {
263            throw new IllegalArgumentException("coordinate=" + coordinate);
264        }
265    }
266
267    private static void computeDistanceAndBearing(double lat1, double lon1,
268        double lat2, double lon2, float[] results) {
269        // Based on http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf
270        // using the "Inverse Formula" (section 4)
271
272        int MAXITERS = 20;
273        // Convert lat/long to radians
274        lat1 *= Math.PI / 180.0;
275        lat2 *= Math.PI / 180.0;
276        lon1 *= Math.PI / 180.0;
277        lon2 *= Math.PI / 180.0;
278
279        double a = 6378137.0; // WGS84 major axis
280        double b = 6356752.3142; // WGS84 semi-major axis
281        double f = (a - b) / a;
282        double aSqMinusBSqOverBSq = (a * a - b * b) / (b * b);
283
284        double L = lon2 - lon1;
285        double A = 0.0;
286        double U1 = Math.atan((1.0 - f) * Math.tan(lat1));
287        double U2 = Math.atan((1.0 - f) * Math.tan(lat2));
288
289        double cosU1 = Math.cos(U1);
290        double cosU2 = Math.cos(U2);
291        double sinU1 = Math.sin(U1);
292        double sinU2 = Math.sin(U2);
293        double cosU1cosU2 = cosU1 * cosU2;
294        double sinU1sinU2 = sinU1 * sinU2;
295
296        double sigma = 0.0;
297        double deltaSigma = 0.0;
298        double cosSqAlpha = 0.0;
299        double cos2SM = 0.0;
300        double cosSigma = 0.0;
301        double sinSigma = 0.0;
302        double cosLambda = 0.0;
303        double sinLambda = 0.0;
304
305        double lambda = L; // initial guess
306        for (int iter = 0; iter < MAXITERS; iter++) {
307            double lambdaOrig = lambda;
308            cosLambda = Math.cos(lambda);
309            sinLambda = Math.sin(lambda);
310            double t1 = cosU2 * sinLambda;
311            double t2 = cosU1 * sinU2 - sinU1 * cosU2 * cosLambda;
312            double sinSqSigma = t1 * t1 + t2 * t2; // (14)
313            sinSigma = Math.sqrt(sinSqSigma);
314            cosSigma = sinU1sinU2 + cosU1cosU2 * cosLambda; // (15)
315            sigma = Math.atan2(sinSigma, cosSigma); // (16)
316            double sinAlpha = (sinSigma == 0) ? 0.0 :
317                cosU1cosU2 * sinLambda / sinSigma; // (17)
318            cosSqAlpha = 1.0 - sinAlpha * sinAlpha;
319            cos2SM = (cosSqAlpha == 0) ? 0.0 :
320                cosSigma - 2.0 * sinU1sinU2 / cosSqAlpha; // (18)
321
322            double uSquared = cosSqAlpha * aSqMinusBSqOverBSq; // defn
323            A = 1 + (uSquared / 16384.0) * // (3)
324                (4096.0 + uSquared *
325                 (-768 + uSquared * (320.0 - 175.0 * uSquared)));
326            double B = (uSquared / 1024.0) * // (4)
327                (256.0 + uSquared *
328                 (-128.0 + uSquared * (74.0 - 47.0 * uSquared)));
329            double C = (f / 16.0) *
330                cosSqAlpha *
331                (4.0 + f * (4.0 - 3.0 * cosSqAlpha)); // (10)
332            double cos2SMSq = cos2SM * cos2SM;
333            deltaSigma = B * sinSigma * // (6)
334                (cos2SM + (B / 4.0) *
335                 (cosSigma * (-1.0 + 2.0 * cos2SMSq) -
336                  (B / 6.0) * cos2SM *
337                  (-3.0 + 4.0 * sinSigma * sinSigma) *
338                  (-3.0 + 4.0 * cos2SMSq)));
339
340            lambda = L +
341                (1.0 - C) * f * sinAlpha *
342                (sigma + C * sinSigma *
343                 (cos2SM + C * cosSigma *
344                  (-1.0 + 2.0 * cos2SM * cos2SM))); // (11)
345
346            double delta = (lambda - lambdaOrig) / lambda;
347            if (Math.abs(delta) < 1.0e-12) {
348                break;
349            }
350        }
351
352        float distance = (float) (b * A * (sigma - deltaSigma));
353        results[0] = distance;
354        if (results.length > 1) {
355            float initialBearing = (float) Math.atan2(cosU2 * sinLambda,
356                cosU1 * sinU2 - sinU1 * cosU2 * cosLambda);
357            initialBearing *= 180.0 / Math.PI;
358            results[1] = initialBearing;
359            if (results.length > 2) {
360                float finalBearing = (float) Math.atan2(cosU1 * sinLambda,
361                    -sinU1 * cosU2 + cosU1 * sinU2 * cosLambda);
362                finalBearing *= 180.0 / Math.PI;
363                results[2] = finalBearing;
364            }
365        }
366    }
367
368    /**
369     * Computes the approximate distance in meters between two
370     * locations, and optionally the initial and final bearings of the
371     * shortest path between them.  Distance and bearing are defined using the
372     * WGS84 ellipsoid.
373     *
374     * <p> The computed distance is stored in results[0].  If results has length
375     * 2 or greater, the initial bearing is stored in results[1]. If results has
376     * length 3 or greater, the final bearing is stored in results[2].
377     *
378     * @param startLatitude the starting latitude
379     * @param startLongitude the starting longitude
380     * @param endLatitude the ending latitude
381     * @param endLongitude the ending longitude
382     * @param results an array of floats to hold the results
383     *
384     * @throws IllegalArgumentException if results is null or has length < 1
385     */
386    public static void distanceBetween(double startLatitude, double startLongitude,
387        double endLatitude, double endLongitude, float[] results) {
388        if (results == null || results.length < 1) {
389            throw new IllegalArgumentException("results is null or has length < 1");
390        }
391        computeDistanceAndBearing(startLatitude, startLongitude,
392            endLatitude, endLongitude, results);
393    }
394
395    /**
396     * Returns the approximate distance in meters between this
397     * location and the given location.  Distance is defined using
398     * the WGS84 ellipsoid.
399     *
400     * @param dest the destination location
401     * @return the approximate distance in meters
402     */
403    public float distanceTo(Location dest) {
404        // See if we already have the result
405        synchronized (mResults) {
406            if (mLatitude != mLat1 || mLongitude != mLon1 ||
407                dest.mLatitude != mLat2 || dest.mLongitude != mLon2) {
408                computeDistanceAndBearing(mLatitude, mLongitude,
409                    dest.mLatitude, dest.mLongitude, mResults);
410                mLat1 = mLatitude;
411                mLon1 = mLongitude;
412                mLat2 = dest.mLatitude;
413                mLon2 = dest.mLongitude;
414                mDistance = mResults[0];
415                mInitialBearing = mResults[1];
416            }
417            return mDistance;
418        }
419    }
420
421    /**
422     * Returns the approximate initial bearing in degrees East of true
423     * North when traveling along the shortest path between this
424     * location and the given location.  The shortest path is defined
425     * using the WGS84 ellipsoid.  Locations that are (nearly)
426     * antipodal may produce meaningless results.
427     *
428     * @param dest the destination location
429     * @return the initial bearing in degrees
430     */
431    public float bearingTo(Location dest) {
432        synchronized (mResults) {
433            // See if we already have the result
434            if (mLatitude != mLat1 || mLongitude != mLon1 ||
435                            dest.mLatitude != mLat2 || dest.mLongitude != mLon2) {
436                computeDistanceAndBearing(mLatitude, mLongitude,
437                    dest.mLatitude, dest.mLongitude, mResults);
438                mLat1 = mLatitude;
439                mLon1 = mLongitude;
440                mLat2 = dest.mLatitude;
441                mLon2 = dest.mLongitude;
442                mDistance = mResults[0];
443                mInitialBearing = mResults[1];
444            }
445            return mInitialBearing;
446        }
447    }
448
449    /**
450     * Returns the name of the provider that generated this fix,
451     * or null if it is not associated with a provider.
452     */
453    public String getProvider() {
454        return mProvider;
455    }
456
457    /**
458     * Sets the name of the provider that generated this fix.
459     */
460    public void setProvider(String provider) {
461        mProvider = provider;
462    }
463
464    /**
465     * Return the UTC time of this fix, in milliseconds since January 1,
466     * 1970.
467     * <p>Note that the UTC time on a device is not monotonic: it
468     * can jump forwards or backwards unpredictably. So always use
469     * {@link #getElapsedRealtimeNano()} when calculating time deltas.
470     * <p>On the other hand, {@link #getTime()} is useful for presenting
471     * a human readable time to the user, or for carefully comparing
472     * location fixes across reboot or across devices.
473     * <p>This method will always return a valid timestamp on
474     * Locations generated by a {@link LocationProvider}.
475     *
476     * @return time of fix, in milliseconds since January 1, 1970.
477     */
478    public long getTime() {
479        return mTime;
480    }
481
482    /**
483     * Set the UTC time of this fix, in milliseconds since January 1,
484     * 1970.
485     *
486     * @param time UTC time of this fix, in milliseconds since January 1, 1970
487     */
488    public void setTime(long time) {
489        mTime = time;
490    }
491
492    /**
493     * Return the time of this fix, in elapsed real-time since system boot.
494     * <p>This value can be reliably compared to
495     * {@link android.os.SystemClock#elapsedRealtimeNano()},
496     * to calculate the age of a fix, and to compare Location fixes, since
497     * elapsed real-time is guaranteed monotonic for each system boot, and
498     * continues to increment even when the system is in deep sleep.
499     * <p>This method will always return a valid timestamp on
500     * Locations generated by a {@link LocationProvider}.
501     *
502     * @return elapsed real-time of fix, in nanoseconds since system boot.
503     */
504    public long getElapsedRealtimeNano() {
505        return mElapsedRealtimeNano;
506    }
507
508    /**
509     * Set the time of this fix, in elapsed real-time since system boot.
510     *
511     * @param time elapsed real-time of fix, in nanoseconds since system boot.
512     */
513    public void setElapsedRealtimeNano(long time) {
514        mElapsedRealtimeNano = time;
515    }
516
517    /**
518     * Return the latitude of this fix.
519     * <p>This method will always return a valid latitude on
520     * Locations generated by a {@link LocationProvider}.
521     */
522    public double getLatitude() {
523        return mLatitude;
524    }
525
526    /**
527     * Sets the latitude of this fix.
528     */
529    public void setLatitude(double latitude) {
530        mLatitude = latitude;
531    }
532
533    /**
534     * Return the longitude of this fix.
535     * <p>This method will always return a valid longitude on
536     * Locations generated by a {@link LocationProvider}.
537     */
538    public double getLongitude() {
539        return mLongitude;
540    }
541
542    /**
543     * Sets the longitude of this fix.
544     */
545    public void setLongitude(double longitude) {
546        mLongitude = longitude;
547    }
548
549    /**
550     * Returns true if this fix contains altitude information, false
551     * otherwise.
552     */
553    public boolean hasAltitude() {
554        return mHasAltitude;
555    }
556
557    /**
558     * Returns the altitude of this fix.  If {@link #hasAltitude} is false,
559     * 0.0f is returned.
560     */
561    public double getAltitude() {
562        return mAltitude;
563    }
564
565    /**
566     * Sets the altitude of this fix.  Following this call,
567     * hasAltitude() will return true.
568     */
569    public void setAltitude(double altitude) {
570        mAltitude = altitude;
571        mHasAltitude = true;
572    }
573
574    /**
575     * Clears the altitude of this fix.  Following this call,
576     * hasAltitude() will return false.
577     */
578    public void removeAltitude() {
579        mAltitude = 0.0f;
580        mHasAltitude = false;
581    }
582
583    /**
584     * Returns true if this fix contains speed information, false
585     * otherwise.  The default implementation returns false.
586     */
587    public boolean hasSpeed() {
588        return mHasSpeed;
589    }
590
591    /**
592     * Returns the speed of the device over ground in meters/second.
593     * If hasSpeed() is false, 0.0f is returned.
594     */
595    public float getSpeed() {
596        return mSpeed;
597    }
598
599    /**
600     * Sets the speed of this fix, in meters/second.  Following this
601     * call, hasSpeed() will return true.
602     */
603    public void setSpeed(float speed) {
604        mSpeed = speed;
605        mHasSpeed = true;
606    }
607
608    /**
609     * Clears the speed of this fix.  Following this call, hasSpeed()
610     * will return false.
611     */
612    public void removeSpeed() {
613        mSpeed = 0.0f;
614        mHasSpeed = false;
615    }
616
617    /**
618     * Returns true if the provider is able to report bearing information,
619     * false otherwise.  The default implementation returns false.
620     */
621    public boolean hasBearing() {
622        return mHasBearing;
623    }
624
625    /**
626     * Returns the direction of travel in degrees East of true
627     * North. If hasBearing() is false, 0.0 is returned.
628     */
629    public float getBearing() {
630        return mBearing;
631    }
632
633    /**
634     * Sets the bearing of this fix.  Following this call, hasBearing()
635     * will return true.
636     */
637    public void setBearing(float bearing) {
638        while (bearing < 0.0f) {
639            bearing += 360.0f;
640        }
641        while (bearing >= 360.0f) {
642            bearing -= 360.0f;
643        }
644        mBearing = bearing;
645        mHasBearing = true;
646    }
647
648    /**
649     * Clears the bearing of this fix.  Following this call, hasBearing()
650     * will return false.
651     */
652    public void removeBearing() {
653        mBearing = 0.0f;
654        mHasBearing = false;
655    }
656
657    /**
658     * Return true if this Location has an associated accuracy.
659     * <p>All Location objects generated by a {@link LocationProvider}
660     * will have an accuracy.
661     */
662    public boolean hasAccuracy() {
663        return mHasAccuracy;
664    }
665
666    /**
667     * Return the accuracy of this Location fix.
668     * <p>Accuracy is measured in meters, and indicates the
669     * radius of 95% confidence.
670     * In other words, there is a 95% probability that the
671     * true location is within a circle centered at the reported
672     * location, with radius of the reported accuracy.
673     * <p>This is only a measure of horizontal accuracy, and does
674     * not indicate the accuracy of bearing, velocity or altitude
675     * if those are included in this Location.
676     * <p>If {@link #hasAccuracy} is false, 0.0 is returned.
677     * <p>All Location object generated by a {@link LocationProvider}
678     * will have a valid accuracy.
679     */
680    public float getAccuracy() {
681        return mAccuracy;
682    }
683
684    /**
685     * Sets the accuracy of this fix.  Following this call, hasAccuracy()
686     * will return true.
687     */
688    public void setAccuracy(float accuracy) {
689        mAccuracy = accuracy;
690        mHasAccuracy = true;
691    }
692
693    /**
694     * Clears the accuracy of this fix.  Following this call, hasAccuracy()
695     * will return false.
696     */
697    public void removeAccuracy() {
698        mAccuracy = 0.0f;
699        mHasAccuracy = false;
700    }
701
702    /**
703     * Return true if this Location object has enough data set to
704     * be considered a valid fix from a {@link LocationProvider}.
705     * @see #makeComplete
706     * @hide
707     */
708    public boolean isComplete() {
709        if (mProvider == null) return false;
710        if (!mHasAccuracy) return false;
711        if (mTime == 0) return false;
712        if (mElapsedRealtimeNano == 0) return false;
713        return true;
714    }
715
716    /**
717     * Helper to fill in incomplete fields.
718     * Only use this to assist in backwards compatibility
719     * with Location objects received from applications.
720     * @see #isComplete
721     * @hide
722     */
723    public void makeComplete() {
724        if (mProvider == null) mProvider = "?";
725        if (!mHasAccuracy) {
726            mHasAccuracy = true;
727            mAccuracy = 100.0f;
728        }
729        if (mTime == 0) mTime = System.currentTimeMillis();
730        if (mElapsedRealtimeNano == 0) mElapsedRealtimeNano = SystemClock.elapsedRealtimeNano();
731    }
732
733    /**
734     * Returns additional provider-specific information about the
735     * location fix as a Bundle.  The keys and values are determined
736     * by the provider.  If no additional information is available,
737     * null is returned.
738     *
739     * <p> A number of common key/value pairs are listed
740     * below. Providers that use any of the keys on this list must
741     * provide the corresponding value as described below.
742     *
743     * <ul>
744     * <li> satellites - the number of satellites used to derive the fix
745     * </ul>
746     */
747    public Bundle getExtras() {
748        return mExtras;
749    }
750
751    /**
752     * Sets the extra information associated with this fix to the
753     * given Bundle.
754     */
755    public void setExtras(Bundle extras) {
756        mExtras = (extras == null) ? null : new Bundle(extras);
757    }
758
759    @Override
760    public String toString() {
761        StringBuilder s = new StringBuilder();
762        s.append("Location[");
763        s.append(mProvider);
764        s.append(String.format(" %.6f,%.6f", mLatitude, mLongitude));
765        if (mHasAccuracy) s.append(String.format(" acc=%.0f", mAccuracy));
766        else s.append(" acc=???");
767        if (mTime == 0) {
768            s.append(" t=?!?");
769        }
770        if (mElapsedRealtimeNano == 0) {
771            s.append(" et=?!?");
772        } else {
773            long age = SystemClock.elapsedRealtimeNano() - mElapsedRealtimeNano;
774            s.append(" age=");
775            TimeUtils.formatDuration(age / 1000000L, s);
776        }
777        if (mHasAltitude) s.append(" alt=").append(mAltitude);
778        if (mHasSpeed) s.append(" vel=").append(mSpeed);
779        if (mHasBearing) s.append(" bear=").append(mBearing);
780
781        if (mExtras != null) {
782            s.append(" {").append(mExtras).append('}');
783        }
784        s.append(']');
785        return s.toString();
786    }
787
788    /**
789     * @deprecated Use {@link #toString} instead
790     */
791    @Deprecated
792    public void dump(Printer pw, String prefix) {
793        pw.println(prefix + toString());
794    }
795
796    public static final Parcelable.Creator<Location> CREATOR =
797        new Parcelable.Creator<Location>() {
798        @Override
799        public Location createFromParcel(Parcel in) {
800            String provider = in.readString();
801            Location l = new Location(provider);
802            l.mTime = in.readLong();
803            l.mElapsedRealtimeNano = in.readLong();
804            l.mLatitude = in.readDouble();
805            l.mLongitude = in.readDouble();
806            l.mHasAltitude = in.readInt() != 0;
807            l.mAltitude = in.readDouble();
808            l.mHasSpeed = in.readInt() != 0;
809            l.mSpeed = in.readFloat();
810            l.mHasBearing = in.readInt() != 0;
811            l.mBearing = in.readFloat();
812            l.mHasAccuracy = in.readInt() != 0;
813            l.mAccuracy = in.readFloat();
814            l.mExtras = in.readBundle();
815            return l;
816        }
817
818        @Override
819        public Location[] newArray(int size) {
820            return new Location[size];
821        }
822    };
823
824    @Override
825    public int describeContents() {
826        return 0;
827    }
828
829    @Override
830    public void writeToParcel(Parcel parcel, int flags) {
831        parcel.writeString(mProvider);
832        parcel.writeLong(mTime);
833        parcel.writeLong(mElapsedRealtimeNano);
834        parcel.writeDouble(mLatitude);
835        parcel.writeDouble(mLongitude);
836        parcel.writeInt(mHasAltitude ? 1 : 0);
837        parcel.writeDouble(mAltitude);
838        parcel.writeInt(mHasSpeed ? 1 : 0);
839        parcel.writeFloat(mSpeed);
840        parcel.writeInt(mHasBearing ? 1 : 0);
841        parcel.writeFloat(mBearing);
842        parcel.writeInt(mHasAccuracy ? 1 : 0);
843        parcel.writeFloat(mAccuracy);
844        parcel.writeBundle(mExtras);
845    }
846}
847