Merge "fix the threading issue for setBuffercount()" into kraken
diff --git a/api/current.xml b/api/current.xml
index 54cd3fb..3e37035 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -81605,6 +81605,28 @@
visibility="public"
>
</method>
+<method name="getBearingAccuracy"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getHorizontalAccuracy"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getPowerRequirement"
return="int"
abstract="false"
@@ -81616,6 +81638,39 @@
visibility="public"
>
</method>
+<method name="getPriority"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSpeedAccuracy"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getVerticalAccuracy"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="isAltitudeRequired"
return="boolean"
abstract="false"
@@ -81686,6 +81741,19 @@
<parameter name="altitudeRequired" type="boolean">
</parameter>
</method>
+<method name="setBearingAccuracy"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="accuracy" type="int">
+</parameter>
+</method>
<method name="setBearingRequired"
return="void"
abstract="false"
@@ -81712,6 +81780,19 @@
<parameter name="costAllowed" type="boolean">
</parameter>
</method>
+<method name="setHorizontalAccuracy"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="accuracy" type="int">
+</parameter>
+</method>
<method name="setPowerRequirement"
return="void"
abstract="false"
@@ -81725,6 +81806,32 @@
<parameter name="level" type="int">
</parameter>
</method>
+<method name="setPreferredPriority"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="priority" type="int">
+</parameter>
+</method>
+<method name="setSpeedAccuracy"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="accuracy" type="int">
+</parameter>
+</method>
<method name="setSpeedRequired"
return="void"
abstract="false"
@@ -81738,6 +81845,19 @@
<parameter name="speedRequired" type="boolean">
</parameter>
</method>
+<method name="setVerticalAccuracy"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="accuracy" type="int">
+</parameter>
+</method>
<method name="writeToParcel"
return="void"
abstract="false"
@@ -81753,6 +81873,17 @@
<parameter name="flags" type="int">
</parameter>
</method>
+<field name="ACCURACY_BEST"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="ACCURACY_COARSE"
type="int"
transient="false"
@@ -81775,6 +81906,50 @@
visibility="public"
>
</field>
+<field name="ACCURACY_HIGH"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACCURACY_LOW"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACCURACY_MEDIUM"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="BEARING_ACCURACY_PRIORITY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="CREATOR"
type="android.os.Parcelable.Creator"
transient="false"
@@ -81785,6 +81960,17 @@
visibility="public"
>
</field>
+<field name="HORIZONTAL_ACCURACY_PRIORITY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="NO_REQUIREMENT"
type="int"
transient="false"
@@ -81829,6 +82015,39 @@
visibility="public"
>
</field>
+<field name="POWER_REQUIREMENT_PRIORITY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SPEED_ACCURACY_PRIORITY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="VERTICAL_ACCURACY_PRIORITY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
</class>
<class name="Geocoder"
extends="java.lang.Object"
@@ -81922,6 +82141,75 @@
</exception>
</method>
</class>
+<class name="GeocoderParams"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getClientPackage"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getLocale"
+ return="java.util.Locale"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="parcel" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
<class name="GpsSatellite"
extends="java.lang.Object"
abstract="false"
@@ -83079,6 +83367,27 @@
deprecated="not deprecated"
visibility="public"
>
+<parameter name="minTime" type="long">
+</parameter>
+<parameter name="minDistance" type="float">
+</parameter>
+<parameter name="criteria" type="android.location.Criteria">
+</parameter>
+<parameter name="listener" type="android.location.LocationListener">
+</parameter>
+<parameter name="looper" type="android.os.Looper">
+</parameter>
+</method>
+<method name="requestLocationUpdates"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
<parameter name="provider" type="java.lang.String">
</parameter>
<parameter name="minTime" type="long">
@@ -83088,6 +83397,89 @@
<parameter name="intent" type="android.app.PendingIntent">
</parameter>
</method>
+<method name="requestLocationUpdates"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="minTime" type="long">
+</parameter>
+<parameter name="minDistance" type="float">
+</parameter>
+<parameter name="criteria" type="android.location.Criteria">
+</parameter>
+<parameter name="intent" type="android.app.PendingIntent">
+</parameter>
+</method>
+<method name="requestSingleUpdate"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="provider" type="java.lang.String">
+</parameter>
+<parameter name="listener" type="android.location.LocationListener">
+</parameter>
+<parameter name="looper" type="android.os.Looper">
+</parameter>
+</method>
+<method name="requestSingleUpdate"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="criteria" type="android.location.Criteria">
+</parameter>
+<parameter name="listener" type="android.location.LocationListener">
+</parameter>
+<parameter name="looper" type="android.os.Looper">
+</parameter>
+</method>
+<method name="requestSingleUpdate"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="provider" type="java.lang.String">
+</parameter>
+<parameter name="intent" type="android.app.PendingIntent">
+</parameter>
+</method>
+<method name="requestSingleUpdate"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="criteria" type="android.location.Criteria">
+</parameter>
+<parameter name="intent" type="android.app.PendingIntent">
+</parameter>
+</method>
<method name="sendExtraCommand"
return="boolean"
abstract="false"
@@ -83398,6 +83790,390 @@
</field>
</class>
</package>
+<package name="android.location.provider"
+>
+<class name="GeocodeProvider"
+ extends="java.lang.Object"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="GeocodeProvider"
+ type="android.location.provider.GeocodeProvider"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="getBinder"
+ return="android.os.IBinder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onGetFromLocation"
+ return="java.lang.String"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="latitude" type="double">
+</parameter>
+<parameter name="longitude" type="double">
+</parameter>
+<parameter name="maxResults" type="int">
+</parameter>
+<parameter name="params" type="android.location.GeocoderParams">
+</parameter>
+<parameter name="addrs" type="java.util.List<android.location.Address>">
+</parameter>
+</method>
+<method name="onGetFromLocationName"
+ return="java.lang.String"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="locationName" type="java.lang.String">
+</parameter>
+<parameter name="lowerLeftLatitude" type="double">
+</parameter>
+<parameter name="lowerLeftLongitude" type="double">
+</parameter>
+<parameter name="upperRightLatitude" type="double">
+</parameter>
+<parameter name="upperRightLongitude" type="double">
+</parameter>
+<parameter name="maxResults" type="int">
+</parameter>
+<parameter name="params" type="android.location.GeocoderParams">
+</parameter>
+<parameter name="addrs" type="java.util.List<android.location.Address>">
+</parameter>
+</method>
+</class>
+<class name="LocationProvider"
+ extends="java.lang.Object"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="LocationProvider"
+ type="android.location.provider.LocationProvider"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="getBinder"
+ return="android.os.IBinder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onAddListener"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uid" type="int">
+</parameter>
+</method>
+<method name="onDisable"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onEnable"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onEnableLocationTracking"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="enable" type="boolean">
+</parameter>
+</method>
+<method name="onGetAccuracy"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onGetInternalState"
+ return="java.lang.String"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onGetPowerRequirement"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onGetStatus"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="extras" type="android.os.Bundle">
+</parameter>
+</method>
+<method name="onGetStatusUpdateTime"
+ return="long"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onHasMonetaryCost"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onMeetsCriteria"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="criteria" type="android.location.Criteria">
+</parameter>
+</method>
+<method name="onRemoveListener"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uid" type="int">
+</parameter>
+</method>
+<method name="onRequiresCell"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onRequiresNetwork"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onRequiresSatellite"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onSendExtraCommand"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="command" type="java.lang.String">
+</parameter>
+<parameter name="extras" type="android.os.Bundle">
+</parameter>
+</method>
+<method name="onSetMinTime"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="minTime" type="long">
+</parameter>
+</method>
+<method name="onSupportsAltitude"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onSupportsBearing"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onSupportsSpeed"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onUpdateLocation"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="location" type="android.location.Location">
+</parameter>
+</method>
+<method name="onUpdateNetworkState"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="state" type="int">
+</parameter>
+<parameter name="info" type="android.net.NetworkInfo">
+</parameter>
+</method>
+<method name="reportLocation"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="location" type="android.location.Location">
+</parameter>
+</method>
+</class>
+</package>
<package name="android.media"
>
<class name="AsyncPlayer"
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 4405da6..94086fa 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -75,11 +75,17 @@
}
}
- rawSource->start();
+ status_t err = rawSource->start();
+
+ if (err != OK) {
+ fprintf(stderr, "rawSource returned error %d (0x%08x)\n", err, err);
+ return;
+ }
if (gPlaybackAudio) {
AudioPlayer *player = new AudioPlayer(NULL);
player->setSource(rawSource);
+ rawSource.clear();
player->start(true /* sourceAlreadyStarted */);
@@ -90,6 +96,8 @@
delete player;
player = NULL;
+
+ return;
} else if (gReproduceBug >= 3 && gReproduceBug <= 5) {
int64_t durationUs;
CHECK(meta->findInt64(kKeyDuration, &durationUs));
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 46c6cb8..31d9a2a 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -1845,21 +1845,19 @@
* #autoFocus(AutoFocusCallback)}, {@link #cancelAutoFocus}, or {@link
* #startPreview()}. Applications can call {@link #getParameters()}
* and this method anytime to get the latest focus distances. If the
- * focus mode is FOCUS_MODE_EDOF, the values may be all 0, which means
- * focus distance is not applicable. If the focus mode is
- * FOCUS_MODE_CONTINUOUS and autofocus has started, focus distances may
- * change from time to time.
+ * focus mode is FOCUS_MODE_CONTINUOUS and autofocus has started, focus
+ * distances may change from time to time.
*
- * Far focus distance > optimal focus distance > near focus distance. If
- * the far focus distance is infinity, the value will be
+ * Far focus distance >= optimal focus distance >= near focus distance.
+ * If the focus distance is infinity, the value will be
* Float.POSITIVE_INFINITY.
*
* @param output focus distances in meters. output must be a float
* array with three elements. Near focus distance, optimal focus
* distance, and far focus distance will be filled in the array.
- * @see #NEAR_FOCUS_DISTANCE_INDEX
- * @see #OPTIMAL_FOCUS_DISTANCE_INDEX
- * @see #FAR_FOCUS_DISTANCE_INDEX
+ * @see #FOCUS_DISTANCE_NEAR_INDEX
+ * @see #FOCUS_DISTANCE_OPTIMAL_INDEX
+ * @see #FOCUS_DISTANCE_FAR_INDEX
*/
public void getFocusDistances(float[] output) {
if (output == null || output.length != 3) {
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
index 6475655..7392ad4 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
@@ -301,7 +301,7 @@
if (sr.SSID.equals(knownSSID)) {
Log.v(LOG_TAG, "found " + knownSSID + " in the scan result list");
WifiConfiguration config = new WifiConfiguration();
- config.SSID = sr.SSID;
+ config.SSID = convertToQuotedString(sr.SSID);
config.allowedKeyManagement.set(KeyMgmt.NONE);
int networkId = mWifiManager.addNetwork(config);
// Connect to network by disabling others.
@@ -387,6 +387,10 @@
context.sendBroadcast(intent);
}
+ protected static String convertToQuotedString(String string) {
+ return "\"" + string + "\"";
+ }
+
@Override
protected void onDestroy() {
super.onDestroy();
diff --git a/docs/html/guide/practices/compatibility.jd b/docs/html/guide/practices/compatibility.jd
index c91ab7f..bb7a72e 100644
--- a/docs/html/guide/practices/compatibility.jd
+++ b/docs/html/guide/practices/compatibility.jd
@@ -36,15 +36,16 @@
variety of hardware.</p>
<p>Fortunately, Android has built-in tools and support that make it easy for
-your apps to do that, while at the same time maintaining control of what types
-of devices your app is available to. If you do your work properly, users
+your apps to do that, while at the same time letting you maintain control of
+what types of devices your app is available to. With a bit of forethought and
+some minor changes in your app's manifest file, you can ensure that users
whose devices can’t run your app will never see it in the Android Market, and
will not get in trouble by downloading it. This page explains how you can
control which devices have access to your apps, and how to prepare your apps to
make sure they reach the right audience.</p>
-<h3 id="defined">What does “Compatibility” mean?</h3>
+<h3 id="defined">What does “compatibility” mean?</h3>
<p>A device is “Android compatible” if it can correctly run apps written for the
<em>Android execution environment</em>. The exact details of the Android execution
diff --git a/docs/html/guide/practices/screens_support.jd b/docs/html/guide/practices/screens_support.jd
index 45e3e49..2863fb2 100644
--- a/docs/html/guide/practices/screens_support.jd
+++ b/docs/html/guide/practices/screens_support.jd
@@ -171,7 +171,9 @@
<p>The table below lists some of the more common screens supported
by Android and illustrates how the platform maps them to generalized screen
-configurations.</p>
+configurations. Some devices use screens that are not specifically listed
+in the table — the platform maps those to the same set generalized
+screen configurations. </p>
<p class="table-caption" id="screens-table"><strong>Table 1.</strong> Examples of
device screens supported by Android.</p>
diff --git a/docs/html/guide/topics/data/backup.jd b/docs/html/guide/topics/data/backup.jd
index 98f7a4f..74117d6 100644
--- a/docs/html/guide/topics/data/backup.jd
+++ b/docs/html/guide/topics/data/backup.jd
@@ -31,6 +31,7 @@
<li><a href="#RestoreVersion">Checking the Restore Data Version</a></li>
<li><a href="#RequestingBackup">Requesting Backup</a></li>
<li><a href="#RequestingRestore">Requesting Restore</a></li>
+ <li><a href="#DevelopingTesting">Developing and Testing Your Backup Agent</a></li>
</ol>
<h2>Key classes</h2>
@@ -759,3 +760,22 @@
restore operation with the <a href="{@docRoot}guide/developing/tools/bmgr.html">bmgr
tool</a>.</p>
+
+<h2 id="DevelopingTesting">Developing and Testing Your Backup Agent</h2>
+
+<p>To develop and test your backup agent:</p>
+<ul>
+ <li>Set your build target to a platform using API Level 8 or higher</li>
+ <li>Run your application on a suitable Android system image:
+ <ul>
+ <li>If using the emulator, create and use an AVD with the Google APIs add-on (API Level
+8) — the Google APIs add-on is available as an SDK component through the SDK and AVD
+Manager</li>
+ <li>If using a device, the device must be running Android 2.2 or greater and have Android
+Market built in</li>
+ </ul>
+ </li>
+ <li>Test your backup agent using the <a href="{@docRoot}guide/developing/tools/bmgr.html">{@code
+bmgr}</a> tool to initiate backup and restore operations</li>
+</ul>
+
diff --git a/docs/html/index.jd b/docs/html/index.jd
index 230dd4e..0ba5e08 100644
--- a/docs/html/index.jd
+++ b/docs/html/index.jd
@@ -141,18 +141,20 @@
+ "<a href='{@docRoot}sdk/index.html'>SDK starter package</a>."
},
- 'adc2': {
+ 'tv': {
'layout':"imgLeft",
- 'icon':"adc2_s.png",
- 'name':"ADC 2",
- 'img':"adc2_l.png",
- 'title':"Android Developer Challenge 2",
- 'desc': "<p>We're pleased to announce the "
- + "<a href='http://code.google.com/android/adc/gallery_winners.html'>winners "
- + "of the Android Developer Challenge 2</a>! Please see our "
- + "<a href='http://android-developers.blogspot.com/2009/11/announcing-winners-of-adc-2.html'>blog post</a> "
- + "for full information. Thanks to everyone who participated by submitting an application or judging!</p>"
- + "<p><a href='http://code.google.com/android/adc/'>Learn more about ADC 2 »</a></p>"
+ 'icon':"tv_s.png",
+ 'name':"Google TV",
+ 'img':"tv_l.png",
+ 'title':"Announcing Google TV!",
+ 'desc': "<p><a href='http://www.google.com/tv/'>Google TV</a> is a new platform "
+ + "for television built on Android. Google "
+ + "has partnered with Sony and Logitech to integrate "
+ + "this platform into TVs, blu-ray players, and companion "
+ + "boxes. An add-on for the Android SDK will be "
+ + "available early next year. </p>"
+ + "<p><a href='http://www.google.com/tv/'>Learn more about "
+ + "Google TV »</a></p>"
},
'devphone': {
diff --git a/docs/html/sdk/android-1.5.jd b/docs/html/sdk/android-1.5.jd
index ab74631..1d6e0ad 100644
--- a/docs/html/sdk/android-1.5.jd
+++ b/docs/html/sdk/android-1.5.jd
@@ -10,25 +10,31 @@
<div id="qv-wrapper">
<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#features">Platform Highlights</a></li>
- <li><a href="#relnotes">Revisions</a></li>
- <li><a href="#apps">Built-in Applications</a></li>
- <li><a href="#locs">Locales</a></li>
- <li><a href="#skins">Emulator Skins</a></li>
- <li><a href="#api">Framework API</a>
- <ol>
- <li><a href="#api-level">API level</a></li>
- <li><a href="#api-changes">API changes summary</a></li>
- <li><a href="{@docRoot}sdk/api_diff/{@sdkPlatformApiLevel}/changes.html">API differences report »</a> </li>
- </ol></li>
- </ol>
+<h2>In this document</h2>
+<ol>
+ <li><a href="#features">Platform Highlights</a></li>
+ <li><a href="#relnotes">Revisions</a></li>
+ <li><a href="#api-level">API Level</a></li>
+ <li><a href="#api">Framework API Changes</a>
+ <li><a href="#apps">Built-in Applications</a></li>
+ <li><a href="#locs">Locales</a></li>
+ <li><a href="#skins">Emulator Skins</a></li>
- <h2>See Also</h2>
- <ol>
- <li><a href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a></li>
- </ol>
+ </ol>
+ </li>
+</ol>
+
+<h2>Reference</h2>
+<ol>
+<li><a
+href="{@docRoot}sdk/api_diff/{@sdkPlatformApiLevel}/changes.html">API
+Differences Report »</a> </li>
+</ol>
+
+<h2>See Also</h2>
+<ol>
+ <li><a href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a></li>
+</ol>
</div>
</div>
@@ -87,7 +93,8 @@
</script>
<style>
.toggleable {
-padding: .25em 1em;
+ padding: .25em 1em 0em 1em;
+ margin-bottom: 0;
}
.toggleme {
padding: 1em 1em 0 2em;
@@ -162,6 +169,106 @@
</div>
</div>
+
+<h2 id="api-level">API Level</h2>
+
+<p>The Android {@sdkPlatformVersion} platform delivers an updated version of
+the framework API. The Android {@sdkPlatformVersion} API
+is assigned an integer identifier —
+<strong>{@sdkPlatformApiLevel}</strong> — that is
+stored in the system itself. This identifier, called the "API Level", allows the
+system to correctly determine whether an application is compatible with
+the system, prior to installing the application. </p>
+
+<p>To use APIs introduced in Android {@sdkPlatformVersion} in your
+application, you need to set the proper value, "{@sdkPlatformApiLevel}", in the
+<code>android:minSdkVersion</code> attributes of the <code><uses-sdk></code>
+element in your application's manifest. </p>
+
+<p>For more information about how to use API Level, see the <a
+href="{@docRoot}guide/appendix/api-levels.html">API Levels</a> document. </p>
+
+
+<h2 id="api">Framework API Changes</h2>
+
+<p>The sections below provide information about the application framework API provided by the Android {@sdkPlatformVersion} platform. </p>
+
+<h3>UI framework</h3>
+ <ul>
+ <li>Framework for easier background/UI thread interaction</li>
+ <li>New {@link android.widget.SlidingDrawer SlidingDrawer} widget</li>
+ <li>New {@link android.widget.HorizontalScrollView HorizontalScrollview} widget</li>
+ </ul>
+
+<h3>AppWidget framework</h3>
+ <ul>
+ <li>APIs for creating secure home screen {@link android.appwidget
+AppWidgets}. For information about how to use AppWidgets, see the Developer's
+Guide <a href="{@docRoot}guide/topics/appwidgets/index.html">AppWidgets</a>
+documentation. Also see <a
+href="http://android-developers.blogspot.com/2009/04/introducing-home-screen-widgets-and.html">
+Introducing home screen widgets and the AppWidget
+framework</a> on the Android Developer's Blog.</li>
+ <li>APIs for populating {@link android.provider.LiveFolders Live Folders}
+ with custom content.</li>
+ </ul>
+
+<h3>Media framework</h3>
+ <ul>
+ <li>Raw audio recording and playback APIs</li>
+ <li>Interactive MIDI playback engine</li>
+ <li>Video recording APIs for developers (3GP format)</li>
+ <li>Video and photo sharing Intents</li>
+ <li>Media search Intent</li>
+ </ul>
+
+<h3>Input Method framework </h3>
+ <ul>
+ <li>{@link android.inputmethodservice.InputMethodService Input Method
+ Service} framework</li>
+ <li>Text-prediction engine</li>
+ <li>Ability to provide downloadable IMEs to users</li>
+ </ul>
+
+<h3>Application-defined hardware requirements</h3>
+
+<p>Applications can now use a new element in their manifest files, <a
+href="{@docRoot}guide/topics/manifest/uses-configuration-element.html"><code><uses-configuration></code></a>
+ to indicate to the Android system what hardware features
+they require in order to function properly. For example, an application might
+use the element to specify that it requires a physical keyboard or a particular
+navigation device, such as a trackball. Prior to installing the application, the
+Android system checks the attributes defined for the
+<code><uses-configuration></code> element and allows the installation to
+continue only if the required hardware is present.</p>
+
+<h3>Speech recognition framework</h3>
+ <ul>
+ <li>Support for using speech recognition libraries via Intent. See {@link
+android.speech.RecognizerIntent RecognizerIntent}.</li>
+ </ul>
+
+<h3>Miscellaneous API additions</h3>
+ <ul>
+ <li>LocationManager - Applications can get location change updates via
+ Intent</li>
+ <li>WebView - Touch start/end/move/cancel DOM event support</li>
+ <li>Redesigned {@link android.hardware.SensorManager Sensor Manager
+ APIs}</li>
+ <li>GLSurfaceView - convenience framework for creating OpenGL
+ applications</li>
+ <li>Broadcast Intent for app update install succeeded - for smoother app
+ upgrade experience</li>
+ </ul>
+
+
+<h3 id="api-diff">API differences report</h3>
+
+<p>For a detailed view of API changes in Android {@sdkPlatformVersion} (API Level {@sdkPlatformApiLevel}), as compared to
+the previous version, see the <a href="{@docRoot}sdk/api_diff/{@sdkPlatformApiLevel}/changes.html">API
+Differences Report</a>.</p>
+
+
<h2 id="apps">Built-in Applications</h2>
<p>The system image included in the downloadable platform provides these
@@ -268,101 +375,3 @@
</ul>
<p>For more information about how to develop an application that displays and functions properly on all Android-powered devices, see <a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a>.</p>
-
-
-<h2 id="api">Framework API</h2>
-
-<p>The sections below provide information about the application framework API provided by the Android {@sdkPlatformVersion} platform. </p>
-
-
-<h3 id="api-level">API level</h3>
-
-<p>The Android {@sdkPlatformVersion} platform delivers an updated version of the framework
-API. As with previous versions, the Android {@sdkPlatformVersion} API
-is assigned an integer identifier — <strong>{@sdkPlatformApiLevel}</strong> — that is
-stored in the system itself. This identifier, called the "API Level", allows the
-system to correctly determine whether an application is compatible with
-the system, prior to installing the application. </p>
-
-<p>To use APIs introduced in Android {@sdkPlatformVersion} in your application, you need to
-set the proper value, "{@sdkPlatformApiLevel}", in the attributes of the <code><uses-sdk></code>
-element in your application's manifest. </p>
-
-<p>For more information about how to use API Level, see the <a
-href="{@docRoot}guide/appendix/api-levels.html">API Levels</a> document. </p>
-
-<h3 id="api-changes">API changes summary</h3>
-
-<h4>UI framework</h4>
- <ul>
- <li>Framework for easier background/UI thread interaction</li>
- <li>New {@link android.widget.SlidingDrawer SlidingDrawer} widget</li>
- <li>New {@link android.widget.HorizontalScrollView HorizontalScrollview} widget</li>
- </ul>
-
-<h4>AppWidget framework</h4>
- <ul>
- <li>APIs for creating secure home screen {@link android.appwidget
-AppWidgets}. For information about how to use AppWidgets, see the Developer's
-Guide <a href="{@docRoot}guide/topics/appwidgets/index.html">AppWidgets</a>
-documentation. Also see <a
-href="http://android-developers.blogspot.com/2009/04/introducing-home-screen-widgets-and.html">
-Introducing home screen widgets and the AppWidget
-framework</a> on the Android Developer's Blog.</li>
- <li>APIs for populating {@link android.provider.LiveFolders Live Folders}
- with custom content.</li>
- </ul>
-
-<h4>Media framework</h4>
- <ul>
- <li>Raw audio recording and playback APIs</li>
- <li>Interactive MIDI playback engine</li>
- <li>Video recording APIs for developers (3GP format)</li>
- <li>Video and photo sharing Intents</li>
- <li>Media search Intent</li>
- </ul>
-
-<h4>Input Method framework </h4>
- <ul>
- <li>{@link android.inputmethodservice.InputMethodService Input Method
- Service} framework</li>
- <li>Text-prediction engine</li>
- <li>Ability to provide downloadable IMEs to users</li>
- </ul>
-
-<h4>Application-defined hardware requirements</h4>
- <p>Applications can now use a new element in their manifest files, <a
-href="{@docRoot}guide/topics/manifest/uses-configuration-element.html"><code><uses-configuration></code></a>
- to indicate to the Android system what hardware features
-they require in order to function properly. For example, an application might
-use the element to specify that it requires a physical keyboard or a particular
-navigation device, such as a trackball. Prior to installing the application, the
-Android system checks the attributes defined for the
-<code><uses-configuration></code> element and allows the installation to
-continue only if the required hardware is present.</p>
-
-<h4>Speech recognition framework</h4>
- <ul>
- <li>Support for using speech recognition libraries via Intent. See {@link
-android.speech.RecognizerIntent RecognizerIntent}.</li>
- </ul>
-
-<h4>Miscellaneous API additions</h4>
- <ul>
- <li>LocationManager - Applications can get location change updates via
- Intent</li>
- <li>WebView - Touch start/end/move/cancel DOM event support</li>
- <li>Redesigned {@link android.hardware.SensorManager Sensor Manager
- APIs}</li>
- <li>GLSurfaceView - convenience framework for creating OpenGL
- applications</li>
- <li>Broadcast Intent for app update install succeeded - for smoother app
- upgrade experience</li>
- </ul>
-
-
-<h3 id="api-diff">API differences report</h3>
-
-<p>For a detailed view of API changes in Android {@sdkPlatformVersion} (API Level {@sdkPlatformApiLevel}), as compared to
-the previous version, see the <a href="{@docRoot}sdk/api_diff/{@sdkPlatformApiLevel}/changes.html">API
-Differences Report</a>.</p>
diff --git a/docs/html/sdk/android-1.6.jd b/docs/html/sdk/android-1.6.jd
index d7d14e9..c2651b6 100644
--- a/docs/html/sdk/android-1.6.jd
+++ b/docs/html/sdk/android-1.6.jd
@@ -10,25 +10,31 @@
<div id="qv-wrapper">
<div id="qv">
- <h2>In this document</h2>
- <ol>
- <li><a href="#features">Platform Highlights</a></li>
- <li><a href="#relnotes">Revisions</a></li>
- <li><a href="#apps">Built-in Applications</a></li>
- <li><a href="#locs">Locales</a></li>
- <li><a href="#skins">Emulator Skins</a></li>
- <li><a href="#api">Framework API</a>
- <ol>
- <li><a href="#api-level">API level</a></li>
- <li><a href="#api-changes">API changes summary</a></li>
- <li><a href="{@docRoot}sdk/api_diff/{@sdkPlatformApiLevel}/changes.html">API differences report »</a> </li>
- </ol></li>
- </ol>
+<h2>In this document</h2>
+<ol>
+ <li><a href="#features">Platform Highlights</a></li>
+ <li><a href="#relnotes">Revisions</a></li>
+ <li><a href="#api-level">API Level</a></li>
+ <li><a href="#api">Framework API Changes</a>
+ <li><a href="#apps">Built-in Applications</a></li>
+ <li><a href="#locs">Locales</a></li>
+ <li><a href="#skins">Emulator Skins</a></li>
- <h2>See Also</h2>
- <ol>
- <li><a href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a></li>
- </ol>
+ </ol>
+ </li>
+</ol>
+
+<h2>Reference</h2>
+<ol>
+<li><a
+href="{@docRoot}sdk/api_diff/{@sdkPlatformApiLevel}/changes.html">API
+Differences Report »</a> </li>
+</ol>
+
+<h2>See Also</h2>
+<ol>
+ <li><a href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a></li>
+</ol>
</div>
</div>
@@ -89,7 +95,8 @@
</script>
<style>
.toggleable {
-padding: .25em 1em;
+ padding: .25em 1em 0em 1em;
+ margin-bottom: 0;
}
.toggleme {
padding: 1em 1em 0 2em;
@@ -186,6 +193,212 @@
</div>
</div>
+
+<h2 id="api-level">API Level</h2>
+
+<p>The Android {@sdkPlatformVersion} platform delivers an updated version of
+the framework API. The Android {@sdkPlatformVersion} API
+is assigned an integer identifier —
+<strong>{@sdkPlatformApiLevel}</strong> — that is
+stored in the system itself. This identifier, called the "API Level", allows the
+system to correctly determine whether an application is compatible with
+the system, prior to installing the application. </p>
+
+<p>To use APIs introduced in Android {@sdkPlatformVersion} in your
+application, you need to set the proper value, "{@sdkPlatformApiLevel}", in the
+<code>android:minSdkVersion</code> attributes of the <code><uses-sdk></code>
+element in your application's manifest. </p>
+
+<p>For more information about how to use API Level, see the <a
+href="{@docRoot}guide/appendix/api-levels.html">API Levels</a> document. </p>
+
+
+<h2 id="api">Framework API Changes</h2>
+
+<p>The sections below provide information about the application framework API provided by the Android {@sdkPlatformVersion} platform. </p>
+
+<h3 id="UIFramework">UI framework</h3>
+ <ul>
+ <li>New classes in {@link android.view.animation}
+ to control the behavior of animations:
+ <ul>
+ <li><code>AnticipateInterpolator</code></li>
+ <li><code>AnticipateOvershootInterpolator</code></li>
+ <li><code>BounceInterpolator</code></li>
+ <li><code>OvershootInterpolator</code></li>
+ </ul>
+ </li>
+ <li>New XML attribute <code>android:onClick</code> to specify a View's
+<a href="/reference/android/view/View.OnClickListener.html">View.OnClickListener</a>
+from a layout file.
+ </li>
+ <li>New support for dealing with varying screen densities. Density
+information is associated with Bitmap and Canvas for performing the
+correct scaling. The framework will automatically scale bitmaps and
+nine-patches based on the density the resource was found under and the
+density of the screen, etc.
+ </li><p>To use APIs introduced in Android {@sdkPlatformVersion} in your application, you need to
+set the proper value, "{@sdkPlatformApiLevel}", in the attributes of the <code><uses-sdk></code>
+element in your application's manifest. </p>
+ </ul>
+
+<h3>Search framework</h3>
+ <ul>
+ <li>Applications can now expose relevant content to users as search
+suggestions in the Quick Search Box, a new device-wide search capability that is
+accessible from the home screen. To support this, the search framework adds new
+attributes to the searchable metadata file. For complete information, see the
+{@link android.app.SearchManager SearchManager} documentation.
+ </li>
+ </ul>
+
+<h3>Accessibility framework</h3>
+ <ul>
+ <li>New {@link android.view.accessibility android.accessibility} package
+that includes classes for capturing accessibility events and forwarding them to
+an {@link android.accessibilityservice AccessibilityService} handler. </li>
+ <li>New {@link android.accessibilityservice AccessibilityService} package
+that lets your application track user events and provide visual, audible, or
+haptic feedback to the user. </li>
+ </ul>
+
+<h3>Gesture input</h3>
+ <ul>
+ <li>New {@link android.gesture gesture} API for creating, recognizing,
+loading, and saving gestures.</li>
+ </ul>
+
+<h3>Text-to-speech</h3>
+ <ul>
+ <li>New {@link android.speech.tts android.speech.tts} package provides
+classes for synthesizing speech from text, for immediate playback or to create a
+sound file.</li>
+ </ul>
+
+<h3>Graphics</h3>
+ <ul>
+ <li>Classes in {@link android.graphics android.graphics} now support scaling
+for different screen densities.</li>
+ </ul>
+
+<h3>Telephony</h3>
+ <ul>
+ <li>New {@link android.telephony.SmsManager SmsManager} and other classes
+for sending and receiving SMS messages.</li>
+ </ul>
+
+<h3>Utilities</h3>
+ <ul>
+ <li>New {@link android.util.DisplayMetrics DisplayMetrics} fields for
+determining the density of the current device screen.</li>
+ </ul>
+
+<h3 id="AndroidManifest">Android Manifest elements</h3>
+
+ <ul>
+ <li>New <a href="{@docRoot}guide/topics/manifest/supports-screens-element.html">{@code
+ <supports-screens>}</a> element lets you specify the device screen sizes that your
+ application is designed and tested to support, where "size" is a combination
+ of resolution and density. If your application is run on a device whose screen
+ size is not specified in the <code><supports-screen></code> element, the system
+ displays the application in <em>compatibility mode</em>, which performs best-effort scaling
+ of the application UI to match the resolution and density of the screen.
+
+ <p>The attributes available for defining an application's screen support are:
+
+ <ul>
+
+ <li><code>smallScreen</code>: Boolean value that indicates whether the
+ application is designed to run on devices with small screens.
+ Examples: QVGA low density; VGA high density.
+ </li>
+ <li><code>normalScreens</code>: Boolean value that indicates whether
+ the application is designed to run on devices with normal screens.
+ Examples: WQVGA low density; HVGA medium density; WVGA high density.
+ </li>
+ <li><code>largeScreens</code>: Boolean value that indicates whether
+ the application is designed to run on devices with significantly
+ larger screens, such that special care may be required on
+ the application's part to make proper use of the screen area.
+ Examples: VGA medium density; WVGA medium density.
+ </li>
+ <li><code>anyDensity</code>: Boolean value that indicates whether
+ the application can accommodate any screen density.
+ </li>
+ <li><code>resizable</code>: Boolean value that indicates whether
+ the application can expand its layout to fit slightly larger screens.
+ </li>
+ </ul>
+ </p>
+ </li>
+
+ <li>New <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code <uses-feature>}</a>
+ element lets an application specify hardware (or other)
+ features that it requires to function normally. When an application
+ specifies such features, the system allows the application to be installed only
+ on devices that offer the required features. The element supports these
+ attributes:
+ <ul>
+ <li><code>name</code>: The name of the feature required by the application. Currently accepts
+ "android.hardware.camera" and "android.hardware.camera.autofocus" values, which specify that a
+ camera and camera autofocus are required, respectively.</li>
+ <li><code>glEsVersion</code>: Indicates the minimum version of OpenGL ES required.</li>
+ </ul>
+ </li>
+ <li>New attributes for the
+ <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code <uses-sdk>}</a> element:
+ <ul>
+ <li><code>targetSdkVersion</code>: Indicates the API Level that the application is targeting.
+ It is able to run on older versions (down to minSdkVersion), but was explicitly tested to
+ work with the version specified here. Specifying this version allows the platform to
+ disable compatibility code that is not required or enable newer features that are not
+ available to older applications. </li>
+ <li><code>maxSdkVersion</code>: Indicates the maximum API Level on which an application is
+ designed to run. <strong>Important:</strong> Please read the <a
+ href="{@docRoot}guide/topics/manifest/uses-sdk-element.html"><code><uses-sdk></code></a>
+ documentation before using this attribute. </li>
+ </ul>
+ </li>
+
+ </li>
+ </ul>
+
+<h3>New permissions</h3>
+
+ <ul>
+ <li>{@link android.Manifest.permission#CHANGE_WIFI_MULTICAST_STATE
+ CHANGE_WIFI_MULTICAST_STATE}: Allows applications to enter Wi-Fi
+ Multicast mode.
+ </li>
+ <li>{@link android.Manifest.permission#GLOBAL_SEARCH}: Allows the
+ global search system to access the data of a specified content provider.
+ </li>
+ <li>{@link android.Manifest.permission#INSTALL_LOCATION_PROVIDER INSTALL_LOCATION_PROVIDER}:
+ Allows an application to install a location provider into the Location Manager.
+ </li>
+ <li>{@link android.Manifest.permission#READ_HISTORY_BOOKMARKS READ_HISTORY_BOOKMARKS}:
+ Allows an application to read (but not write) the user's browsing history
+ and bookmarks.
+ </li>
+ <li>{@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS WRITE_HISTORY_BOOKMARKS}:
+ Allows an application to write (but not read) the user's browsing history
+ and bookmarks.
+ </li>
+ <li>{@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE WRITE_EXTERNAL_STORAGE}:
+ Allows an application to write to external storage. Applications using API Level 3
+ and lower will be implicitly granted this permission (and this will be visible to
+ the user); Applications using API Level 4 or higher must explicitly request this
+ permission.
+ </li>
+ </ul>
+
+
+<h3 id="api-diff">API differences report</h3>
+
+<p>For a detailed view of API changes in Android {@sdkPlatformVersion} (API Level {@sdkPlatformApiLevel}), as compared to
+the previous version, see the <a href="{@docRoot}sdk/api_diff/{@sdkPlatformApiLevel}/changes.html">API
+Differences Report</a>.</p>
+
<h2 id="apps">Built-in Applications</h2>
<p>The system image included in the downloadable platform provides these
@@ -292,209 +505,3 @@
</ul>
<p>For more information about how to develop an application that displays and functions properly on all Android-powered devices, see <a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a>.</p>
-
-
-<h2 id="api">Framework API</h2>
-
-<p>The sections below provide information about the application framework API provided by the Android {@sdkPlatformVersion} platform. </p>
-
-
-<h3 id="api-level">API level</h3>
-
-<p>The Android {@sdkPlatformVersion} platform delivers an updated version of the framework
-API. As with previous versions, the Android {@sdkPlatformVersion} API
-is assigned an integer identifier — <strong>{@sdkPlatformApiLevel}</strong> — that is
-stored in the system itself. This identifier, called the "API Level", allows the
-system to correctly determine whether an application is compatible with
-the system, prior to installing the application. </p>
-
-<p>To use APIs introduced in Android {@sdkPlatformVersion} in your application, you need to
-set the proper value, "{@sdkPlatformApiLevel}", in the attributes of the <code><uses-sdk></code>
-element in your application's manifest. </p>
-
-<p>For more information about how to use API Level, see the <a
-href="{@docRoot}guide/appendix/api-levels.html">API Levels</a> document. </p>
-
-<h3 id="api-changes">API changes summary</h3>
-
-<h4 id="UIFramework">UI framework</h4>
- <ul>
- <li>New classes in {@link android.view.animation}
- to control the behavior of animations:
- <ul>
- <li><code>AnticipateInterpolator</code></li>
- <li><code>AnticipateOvershootInterpolator</code></li>
- <li><code>BounceInterpolator</code></li>
- <li><code>OvershootInterpolator</code></li>
- </ul>
- </li>
- <li>New XML attribute <code>android:onClick</code> to specify a View's
-<a href="/reference/android/view/View.OnClickListener.html">View.OnClickListener</a>
-from a layout file.
- </li>
- <li>New support for dealing with varying screen densities. Density
-information is associated with Bitmap and Canvas for performing the
-correct scaling. The framework will automatically scale bitmaps and
-nine-patches based on the density the resource was found under and the
-density of the screen, etc.
- </li><p>To use APIs introduced in Android {@sdkPlatformVersion} in your application, you need to
-set the proper value, "{@sdkPlatformApiLevel}", in the attributes of the <code><uses-sdk></code>
-element in your application's manifest. </p>
- </ul>
-
-<h4>Search framework</h4>
- <ul>
- <li>Applications can now expose relevant content to users as search
-suggestions in the Quick Search Box, a new device-wide search capability that is
-accessible from the home screen. To support this, the search framework adds new
-attributes to the searchable metadata file. For complete information, see the
-{@link android.app.SearchManager SearchManager} documentation.
- </li>
- </ul>
-
-<h4>Accessibility framework</h4>
- <ul>
- <li>New {@link android.view.accessibility android.accessibility} package
-that includes classes for capturing accessibility events and forwarding them to
-an {@link android.accessibilityservice AccessibilityService} handler. </li>
- <li>New {@link android.accessibilityservice AccessibilityService} package
-that lets your application track user events and provide visual, audible, or
-haptic feedback to the user. </li>
- </ul>
-
-<h4>Gesture Input</h4>
- <ul>
- <li>New {@link android.gesture gesture} API for creating, recognizing,
-loading, and saving gestures.</li>
- </ul>
-
-<h4>Text-to-speech</h4>
- <ul>
- <li>New {@link android.speech.tts android.speech.tts} package provides
-classes for synthesizing speech from text, for immediate playback or to create a
-sound file.</li>
- </ul>
-
-<h4>Graphics</h4>
- <ul>
- <li>Classes in {@link android.graphics android.graphics} now support scaling
-for different screen densities.</li>
- </ul>
-
-<h4>Telephony</h4>
- <ul>
- <li>New {@link android.telephony.SmsManager SmsManager} and other classes
-for sending and receiving SMS messages.</li>
- </ul>
-
-<h4>Utilities</h4>
- <ul>
- <li>New {@link android.util.DisplayMetrics DisplayMetrics} fields for
-determining the density of the current device screen.</li>
- </ul>
-
-<h4 id="AndroidManifest">Android Manifest elements</h4>
-
- <ul>
- <li>New <a href="{@docRoot}guide/topics/manifest/supports-screens-element.html">{@code
- <supports-screens>}</a> element lets you specify the device screen sizes that your
- application is designed and tested to support, where "size" is a combination
- of resolution and density. If your application is run on a device whose screen
- size is not specified in the <code><supports-screen></code> element, the system
- displays the application in <em>compatibility mode</em>, which performs best-effort scaling
- of the application UI to match the resolution and density of the screen.
-
- <p>The attributes available for defining an application's screen support are:
-
- <ul>
-
- <li><code>smallScreen</code>: Boolean value that indicates whether the
- application is designed to run on devices with small screens.
- Examples: QVGA low density; VGA high density.
- </li>
- <li><code>normalScreens</code>: Boolean value that indicates whether
- the application is designed to run on devices with normal screens.
- Examples: WQVGA low density; HVGA medium density; WVGA high density.
- </li>
- <li><code>largeScreens</code>: Boolean value that indicates whether
- the application is designed to run on devices with significantly
- larger screens, such that special care may be required on
- the application's part to make proper use of the screen area.
- Examples: VGA medium density; WVGA medium density.
- </li>
- <li><code>anyDensity</code>: Boolean value that indicates whether
- the application can accommodate any screen density.
- </li>
- <li><code>resizable</code>: Boolean value that indicates whether
- the application can expand its layout to fit slightly larger screens.
- </li>
- </ul>
- </p>
- </li>
-
- <li>New <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code <uses-feature>}</a>
- element lets an application specify hardware (or other)
- features that it requires to function normally. When an application
- specifies such features, the system allows the application to be installed only
- on devices that offer the required features. The element supports these
- attributes:
- <ul>
- <li><code>name</code>: The name of the feature required by the application. Currently accepts
- "android.hardware.camera" and "android.hardware.camera.autofocus" values, which specify that a
- camera and camera autofocus are required, respectively.</li>
- <li><code>glEsVersion</code>: Indicates the minimum version of OpenGL ES required.</li>
- </ul>
- </li>
- <li>New attributes for the
- <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code <uses-sdk>}</a> element:
- <ul>
- <li><code>targetSdkVersion</code>: Indicates the API Level that the application is targeting.
- It is able to run on older versions (down to minSdkVersion), but was explicitly tested to
- work with the version specified here. Specifying this version allows the platform to
- disable compatibility code that is not required or enable newer features that are not
- available to older applications. </li>
- <li><code>maxSdkVersion</code>: Indicates the maximum API Level on which an application is
- designed to run. <strong>Important:</strong> Please read the <a
- href="{@docRoot}guide/topics/manifest/uses-sdk-element.html"><code><uses-sdk></code></a>
- documentation before using this attribute. </li>
- </ul>
- </li>
-
- </li>
- </ul>
-
-<h4>New Permissions</h4>
-
- <ul>
- <li>{@link android.Manifest.permission#CHANGE_WIFI_MULTICAST_STATE
- CHANGE_WIFI_MULTICAST_STATE}: Allows applications to enter Wi-Fi
- Multicast mode.
- </li>
- <li>{@link android.Manifest.permission#GLOBAL_SEARCH}: Allows the
- global search system to access the data of a specified content provider.
- </li>
- <li>{@link android.Manifest.permission#INSTALL_LOCATION_PROVIDER INSTALL_LOCATION_PROVIDER}:
- Allows an application to install a location provider into the Location Manager.
- </li>
- <li>{@link android.Manifest.permission#READ_HISTORY_BOOKMARKS READ_HISTORY_BOOKMARKS}:
- Allows an application to read (but not write) the user's browsing history
- and bookmarks.
- </li>
- <li>{@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS WRITE_HISTORY_BOOKMARKS}:
- Allows an application to write (but not read) the user's browsing history
- and bookmarks.
- </li>
- <li>{@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE WRITE_EXTERNAL_STORAGE}:
- Allows an application to write to external storage. Applications using API Level 3
- and lower will be implicitly granted this permission (and this will be visible to
- the user); Applications using API Level 4 or higher must explicitly request this
- permission.
- </li>
- </ul>
-
-
-<h3 id="api-diff">API differences report</h3>
-
-<p>For a detailed view of API changes in Android {@sdkPlatformVersion} (API Level {@sdkPlatformApiLevel}), as compared to
-the previous version, see the <a href="{@docRoot}sdk/api_diff/{@sdkPlatformApiLevel}/changes.html">API
-Differences Report</a>.</p>
diff --git a/docs/html/sdk/android-2.1.jd b/docs/html/sdk/android-2.1.jd
index db9c491..7490bae 100644
--- a/docs/html/sdk/android-2.1.jd
+++ b/docs/html/sdk/android-2.1.jd
@@ -13,20 +13,23 @@
<ol>
<li><a href="#features">Platform Highlights</a></li>
<li><a href="#relnotes">Revisions</a></li>
+ <li><a href="#api-level">API Level</a></li>
+ <li><a href="#api">Framework API Changes</a>
<li><a href="#apps">Built-in Applications</a></li>
<li><a href="#locs">Locales</a></li>
<li><a href="#skins">Emulator Skins</a></li>
- <li><a href="#api">Framework API</a>
- <ol>
- <li><a href="#api-level">API level</a></li>
- <li><a href="#api-changes">API changes summary</a></li>
- <li><a
-href="{@docRoot}sdk/api_diff/{@sdkPlatformApiLevel}/changes.html">API
-differences report »</a> </li>
+
</ol>
</li>
</ol>
+<h2>Reference</h2>
+<ol>
+<li><a
+href="{@docRoot}sdk/api_diff/{@sdkPlatformApiLevel}/changes.html">API
+Differences Report »</a> </li>
+</ol>
+
<h2>See Also</h2>
<ol>
<li><a href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a></li>
@@ -90,7 +93,8 @@
</script>
<style>
.toggleable {
-padding: .25em 1em;
+ padding: .25em 1em 0em 1em;
+ margin-bottom: 0;
}
.toggleme {
padding: 1em 1em 0 2em;
@@ -147,6 +151,115 @@
</div>
</div>
+
+<h2 id="api-level">API Level</h2>
+
+<p>The Android {@sdkPlatformVersion} platform delivers an updated version of
+the framework API. The Android {@sdkPlatformVersion} API
+is assigned an integer identifier —
+<strong>{@sdkPlatformApiLevel}</strong> — that is
+stored in the system itself. This identifier, called the "API Level", allows the
+system to correctly determine whether an application is compatible with
+the system, prior to installing the application. </p>
+
+<p>To use APIs introduced in Android {@sdkPlatformVersion} in your
+application, you need to set the proper value, "{@sdkPlatformApiLevel}", in the
+<code>android:minSdkVersion</code> attributes of the <code><uses-sdk></code>
+element in your application's manifest. </p>
+
+<p>For more information about how to use API Level, see the <a
+href="{@docRoot}guide/appendix/api-levels.html">API Levels</a> document. </p>
+
+
+<h2 id="api">Framework API Changes</h2>
+
+<p>The sections below provide information about changes made to the application
+framework API provided by the Android {@sdkPlatformVersion} platform.</p>
+
+<h3>Live Wallpapers</h3>
+
+<p>The following additions provide APIs for you to develop animated wallpapers:</p>
+<ul>
+<li>New {@link android.service.wallpaper} package.</li>
+<li>New {@link android.app.WallpaperInfo} class.</li>
+<li>Updated {@link android.app.WallpaperManager}.</li>
+</ul>
+
+<p>Additionally, if your application uses or provides Live Wallpapers, you must
+remember to add a <a
+href="{@docRoot}guide/topics/manifest/uses-feature-element.html"><code><uses-feature></code></a>
+ element to the application's manifest, declaring the attribute
+<code>android:name="android.software.live_wallpaper"</code>. For example:</p>
+
+<pre class="no-pretty-print">
+<uses-feature android:name="android.software.live_wallpaper" />
+</pre>
+
+<p>When you've published your application, Android Market checks for the
+presence of this element and uses it as a filter, ensuring that your application
+is not made available to users whose devices do not support Live Wallpapers.
+</p>
+
+<h3>Telephony</h3>
+
+<ul>
+<li>New {@link android.telephony.SignalStrength} class provides information
+about the device's current network signal. This can be acquired from the
+new {@link
+android.telephony.PhoneStateListener#onSignalStrengthsChanged(SignalStrength)}
+callback.</li>
+
+<li>New {@link
+android.telephony.PhoneStateListener#onDataConnectionStateChanged(int,int)}
+callback.</li>
+</ul>
+
+<h3>Views</h3>
+
+<ul>
+<li>New {@link android.view.View} methods {@link android.view.View#isOpaque()}
+and {@link android.view.View#onDrawScrollBars(Canvas)}.</li>
+
+<li>New {@link android.widget.RemoteViews} methods {@link
+android.widget.RemoteViews#addView(int,RemoteViews)} and {@link
+android.widget.RemoteViews#removeAllViews(int)}.</li>
+
+<li>New {@link android.view.ViewGroup} methods {@link
+android.view.ViewGroup#isChildrenDrawingOrderEnabled()} and {@link
+android.view.ViewGroup#setChildrenDrawingOrderEnabled(boolean)}.</li>
+</ul>
+
+<h3>WebKit</h3>
+
+<ul>
+<li>New {@link android.webkit.WebStorage} methods to manipulate web
+storage databases.</li>
+
+<li>New {@link android.webkit.GeolocationPermissions} methods to
+get Geolocation permissions from, and set them on the WebView.</li>
+
+<li>New {@link android.webkit.WebSettings} methods to manage settings for
+app cache, web storage, and zooming based on screen density.</li>
+
+<li>New {@link android.webkit.WebChromeClient} methods for handling video,
+browsing history, custom Views, app cache limits, and more.</li>
+</ul>
+
+</ul>
+
+<!--
+<h3 id="behavior-changes">Behavior changes</h3>
+
+<h3 id="bug-fixes">Bug fixes</h3>
+-->
+
+<h3 id="api-diff">API differences report</h3>
+
+<p>For a detailed view of all API changes in Android {@sdkPlatformVersion} (API
+Level {@sdkPlatformApiLevel}), as compared to API Level 6, see the <a
+href="{@docRoot}sdk/api_diff/{@sdkPlatformApiLevel}/changes.html">API
+Differences Report</a>.</p>
+
<h2 id="apps">Built-in Applications</h2>
<p>The system image included in the downloadable platform provides these
@@ -262,117 +375,3 @@
and functions properly on all Android-powered devices, see <a
href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple
Screens</a>.</p>
-
-<h2 id="api">Framework API</h2>
-
-<p>The sections below provide information about changes made to the application
-framework API provided by the Android {@sdkPlatformVersion} platform.</p>
-
-
-<h3 id="api-level">API level</h3>
-
-<p>The Android {@sdkPlatformVersion} platform delivers an updated version of
-the framework API. The Android {@sdkPlatformVersion} API
-is assigned an integer identifier —
-<strong>{@sdkPlatformApiLevel}</strong> — that is
-stored in the system itself. This identifier, called the "API Level", allows the
-system to correctly determine whether an application is compatible with
-the system, prior to installing the application. </p>
-
-<p>To use APIs introduced in Android {@sdkPlatformVersion} in your
-application, you need to set the proper value, "{@sdkPlatformApiLevel}", in the
-attributes of the <code><uses-sdk></code> element in your application's
-manifest. </p>
-
-<p>For more information about how to use API Level, see the <a
-href="{@docRoot}guide/appendix/api-levels.html">API Levels</a> document. </p>
-
-
-<h3 id="api-changes">API changes summary</h3>
-
-<p>The following is a summary of some notable changes to the framework APIs.</p>
-
-<h4>Live Wallpapers</h4>
-
-<p>The following additions provide APIs for you to develop animated wallpapers:</p>
-<ul>
-<li>New {@link android.service.wallpaper} package.</li>
-<li>New {@link android.app.WallpaperInfo} class.</li>
-<li>Updated {@link android.app.WallpaperManager}.</li>
-</ul>
-
-<p>Additionally, if your application uses or provides Live Wallpapers, you must
-remember to add a <a
-href="{@docRoot}guide/topics/manifest/uses-feature-element.html"><code><uses-feature></code></a>
- element to the application's manifest, declaring the attribute
-<code>android:name="android.software.live_wallpaper"</code>. For example:</p>
-
-<pre class="no-pretty-print">
-<uses-feature android:name="android.software.live_wallpaper" />
-</pre>
-
-<p>When you've published your application, Android Market checks for the
-presence of this element and uses it as a filter, ensuring that your application
-is not made available to users whose devices do not support Live Wallpapers.
-</p>
-
-<h4>Telephony</h4>
-
-<ul>
-<li>New {@link android.telephony.SignalStrength} class provides information
-about the device's current network signal. This can be acquired from the
-new {@link
-android.telephony.PhoneStateListener#onSignalStrengthsChanged(SignalStrength)}
-callback.</li>
-
-<li>New {@link
-android.telephony.PhoneStateListener#onDataConnectionStateChanged(int,int)}
-callback.</li>
-</ul>
-
-<h4>Views</h4>
-
-<ul>
-<li>New {@link android.view.View} methods {@link android.view.View#isOpaque()}
-and {@link android.view.View#onDrawScrollBars(Canvas)}.</li>
-
-<li>New {@link android.widget.RemoteViews} methods {@link
-android.widget.RemoteViews#addView(int,RemoteViews)} and {@link
-android.widget.RemoteViews#removeAllViews(int)}.</li>
-
-<li>New {@link android.view.ViewGroup} methods {@link
-android.view.ViewGroup#isChildrenDrawingOrderEnabled()} and {@link
-android.view.ViewGroup#setChildrenDrawingOrderEnabled(boolean)}.</li>
-</ul>
-
-<h4>WebKit</h4>
-
-<ul>
-<li>New {@link android.webkit.WebStorage} methods to manipulate web
-storage databases.</li>
-
-<li>New {@link android.webkit.GeolocationPermissions} methods to
-get Geolocation permissions from, and set them on the WebView.</li>
-
-<li>New {@link android.webkit.WebSettings} methods to manage settings for
-app cache, web storage, and zooming based on screen density.</li>
-
-<li>New {@link android.webkit.WebChromeClient} methods for handling video,
-browsing history, custom Views, app cache limits, and more.</li>
-</ul>
-
-</ul>
-
-<!--
-<h3 id="behavior-changes">Behavior changes</h3>
-
-<h3 id="bug-fixes">Bug fixes</h3>
--->
-
-<h3 id="api-diff">API differences report</h3>
-
-<p>For a detailed view of all API changes in Android {@sdkPlatformVersion} (API
-Level {@sdkPlatformApiLevel}), as compared to API Level 6, see the <a
-href="{@docRoot}sdk/api_diff/{@sdkPlatformApiLevel}/changes.html">API
-Differences Report</a>.</p>
-
diff --git a/docs/html/sdk/android-2.2-highlights.jd b/docs/html/sdk/android-2.2-highlights.jd
new file mode 100644
index 0000000..8bed675
--- /dev/null
+++ b/docs/html/sdk/android-2.2-highlights.jd
@@ -0,0 +1,298 @@
+page.title=Android 2.2 Platform Highlights
+
+@jd:body
+
+
+<style type="text/css">
+#jd-content {
+ max-width:800px;
+}
+#jd-content div.screenshot {
+ float:left;
+ clear:left;
+ padding:15px 30px 15px 0;
+}
+#jd-content div.video {
+ float:right;
+ padding:0 60px 40px;
+ margin-top:-15px;
+}
+#jd-content table.columns {
+ margin:0 0 1em 0;
+}
+#jd-content table.columns td {
+ padding:0;
+}
+#jd-content table.columns td+td {
+ padding:0 2em;
+}
+#jd-content table.columns td img {
+ margin:0;
+}
+#jd-content table.columns td+td>*:first-child {
+ margin-top:-2em;
+}
+.green {
+ color:#8db529;
+ font-weight:bold;
+}
+</style>
+
+
+<div class="video">
+<object width="278 height="180">
+<param name="movie" value="http://www.youtube.com/v/yAZYSVr2Bhc&hl=en&fs=1&"></param>
+<param name="allowFullScreen" value="true"></param><param name="allowscriptaccess"
+value="always"></param>
+<embed src="http://www.youtube.com/v/yAZYSVr2Bhc&hl=en&fs=1&" type="application/x-shockwave-flash"
+allowscriptaccess="always" allowfullscreen="true" width="278" height="180"></embed>
+</object>
+</div>
+
+
+<p>The Android 2.2 platform introduces many new and exciting features for
+users and developers. This document provides a glimpse at some of the new user features
+and technologies in Android 2.2. For more information about the new developer APIs, see the <a
+href="android-2.2.html#api">Android 2.2 version notes</a>.</p>
+
+<ul>
+ <li><a href="#UserFeatures">New User Features</a></li>
+ <li><a href="#PlatformTechnologies">New Platform Technologies</a></li>
+ <li><a href="#DeveloperServices">New Developer Services</a></li>
+ <li><a href="#DeveloperApis">New Developer APIs</a></li>
+</ul>
+
+
+
+<h2 id="UserFeatures" style="clear:right">New User Features</h2>
+
+<h3>Home</h3>
+
+<table class="columns" style="max-width:800px">
+<tr>
+ <td>
+<img src="images/2.2/22home.png" alt="" height=230 />
+ </td>
+ <td>
+<p style="margin-top:0">New Home screen <span class="green">tips widget</span> assists new users on
+how to configure the
+home screen with shortcuts and widgets and how to make use of multiple home screens.</p>
+<p>The Phone, applications Launcher, and Browser now have <span class="green">dedicated
+shortcuts</span> on the Home screen, making it easy to access them from any of the 5 home screen
+panels.</p>
+ </td>
+</tr>
+</table>
+
+
+
+<h3>Exchange support</h3>
+
+<table class="columns" style="max-width:800px">
+<tr>
+ <td>
+<p><span class="green">Improved security</span> with the addition of numeric pin or alpha-numeric
+password options to unlock device. Exchange administrators can enforce password policy across
+devices.</p>
+<p><span class="green">Remote wipe:</span> Exchange administrators can remotely reset the device to
+factory defaults to secure data in case device is lost or stolen.</p>
+<p><span class="green">Exchange Calendars are now supported</span> in the Calendar application.</p>
+<p><span class="green">Auto-discovery:</span> you just need to know your user-name and password to
+easily set up and sync an Exchange account (available for Exchange 2007 and higher).</p>
+<p><span class="green">Global Address Lists look-up</span> is now available in the Email
+application, enabling users to auto-complete recipient names from the directory.</p>
+ </td>
+ <td>
+<img src="images/2.2/22exchange.png" alt="" height=300 />
+ </td>
+</tr>
+</table>
+
+
+<h3>Camera and Gallery</h3>
+
+<table class="columns" style="max-width:800px">
+<tr>
+ <td>
+<img src="images/2.2/22gallery.png" alt="" height=220 />
+ </td>
+ <td>
+<p>Gallery allows you to <span class="green">peek into picture stacks</span> using a zoom
+gesture.</p>
+<p>Camera <span class="green">onscreen buttons</span> provide easy access to a new UI for
+controling zoom, flash, white balance, geo-tagging, focus and exposure. Camcorder also provides
+an easy way to set video size/quality for MMS and YouTube.</p>
+<p>With the <span class="green">LED flash</span> now enabled for the Camcorder, videos can be shot
+at night or in low light settings.</p>
+ </td>
+</tr>
+</table>
+
+
+<h3>Portable hotspot</h3>
+
+<table class="columns" style="max-width:800px">
+<tr>
+ <td>
+<p>Certain devices like the Nexus One can be turned into <span class="green">a portable Wi-Fi
+hotspot</span> that can be shared with up to 8 devices.</p>
+<p>You can use your Android-powered phone as a 3G connection for a Windows or Linux laptop by
+connecting their phone to the computer with a USB cable. The connection is then shared between the
+two devices.</p>
+ </td>
+ <td>
+<img src="images/2.2/22hotspot.png" alt="" height=180 />
+ </td>
+</tr>
+</table>
+
+
+<h3>Multiple keyboard languages</h3>
+
+<table class="columns" style="max-width:800px">
+<tr>
+ <td>
+<img src="images/2.2/22keyboard.png" alt="" height=220 />
+ </td>
+ <td>
+<p>Multi-lingual users can add multiple languages to the keyboard and <span class="green">switch
+between multiple Latin-based input languages</span> by swiping across the space bar. This changes
+the keys as well as the auto-suggest dictionary.</p>
+ </td>
+</tr>
+</table>
+
+
+<h3>Improved performance</h3>
+
+<table class="columns" style="max-width:800px">
+<tr>
+ <td>
+<p><span class="green">Performance of the browser</span> has been enhanced using the V8 engine,
+which enables faster loading of JavaScript-heavy pages.</p>
+<p><span class="green">Dalvik Performance Boost:</span> 2x-5x performance speedup for CPU-heavy code
+over Android 2.1 with Dalvik JIT.</p>
+<p>The graph to the right shows the performance <span class="green">speedup</span> from Android 2.1
+to Android 2.2 using various benchmark tests. For example, LinPack is now more than 5 times
+faster.</p>
+<p><span class="green">Kernel Memory Management Boost:</span> Improved memory reclaim by up to 20x,
+which results in faster app switching and smoother performance on memory-constrained devices.</p>
+ </td>
+ <td>
+<img src="images/2.2/jit-graph.png" alt="" height=200 />
+ </td>
+</tr>
+</table>
+
+
+
+
+
+<h2 id="PlatformTechnologies">New Platform Technologies</h2>
+
+
+<h3>Media framework</h3>
+
+<ul>
+ <li>New media framework (Stagefright) that supports local file playback and HTTP progressive
+streaming</li>
+ <li>Continued support for OpenCore in Android 2.2</li>
+</ul>
+
+
+<h3>Bluetooth</h3>
+
+<ul>
+ <li>Voice dialing over Bluetooth</li>
+ <li>Ability to share contacts with other phones</li>
+ <li>Support for Bluetooth enabled car and desk docks</li>
+ <li>Improved compatibility matrix with car kits and headsets</li>
+</ul>
+
+
+<h3>2.6.32 kernel upgrade</h3>
+
+<ul>
+ <li>HIGHMEM support for RAM >256MB</li>
+ <li>SDIO scheduling and BT improvements</li>
+</ul>
+
+
+
+
+<h2 id="DeveloperServices">New Developer Services</h2>
+
+
+<h3>Android Cloud to Device Messaging</h3>
+
+<p>Apps can utilize Android Cloud to Device Messaging to enable mobile alert, send to phone, and
+two-way push sync functionality.</p>
+
+
+<h3>Android Application Error Reports</h3>
+
+<p>New bug reporting feature for Android Market apps enables developers to receive crash and freeze
+reports from their users. The reports will be available when they log into their publisher
+account.</p>
+
+
+
+
+<h2 id="DeveloperApis">New Developer APIs</h2>
+
+
+<h3>Apps on external storage</h3>
+
+<p>Applications can now request installation on the shared external storage (such as an SD
+card).</p>
+
+
+<h3>Media framework</h3>
+
+<p>Provides new APIs for audio focus, routing audio to SCO, and auto-scan of files to media
+database. Also provides APIs to let applications detect completion of sound loading and auto-pause
+and auto-resume audio playback.</p>
+
+
+<h3>Camera and Camcorder</h3>
+
+<p>New preview API doubles the frame rate from ~10FPS to ~20FPS. Camera now supports portrait
+orientation, zoom controls, access to exposure data, and a thumbnail utility. A new camcorder
+profile enables apps to determine device hardware capablities.</p>
+
+
+<h3>Graphics</h3>
+
+<p>New APIs for OpenGL ES 2.0, working with YUV image format, and ETC1 for texture
+compression.</p>
+
+
+<h3>Data backup</h3>
+
+<p>Apps can participate in data backup and restore, to ensure that users maintain their data
+after performing a factory reset or when switching devices.</p>
+
+
+<h3>Device policy manager</h3>
+
+<p>New device policy management APIs allow developers to write "device administrator" applications
+that can control security features on the device, such as the minimum password strength, data wipe,
+and so on. Users can select the administrators that are enabled on their devices.</p>
+
+
+<h3>UI framework</h3>
+
+<p>New "car mode" and "night mode" controls and configurations allow applications to adjust their UI
+for these situations. A scale gesture detector API provides improved definition of multi-touch
+events. Applications can now customize the bottom strip of a TabWidget.</p>
+
+
+
+<p>For more information about the new developer APIs, see the <a
+href="android-2.2.html#api">Android 2.2 version notes</a> and the <a
+href="{@docRoot}sdk/api_diff/8/changes.html">API Differences Report</a>.</p>
+
+
+
+
+
diff --git a/docs/html/sdk/android-2.2.jd b/docs/html/sdk/android-2.2.jd
index ab261f0..f82edf9 100644
--- a/docs/html/sdk/android-2.2.jd
+++ b/docs/html/sdk/android-2.2.jd
@@ -13,20 +13,23 @@
<ol>
<li><a href="#features">Platform Highlights</a></li>
<li><a href="#relnotes">Revisions</a></li>
+ <li><a href="#api-level">API Level</a></li>
+ <li><a href="#api">Framework API Changes</a>
<li><a href="#apps">Built-in Applications</a></li>
<li><a href="#locs">Locales</a></li>
<li><a href="#skins">Emulator Skins</a></li>
- <li><a href="#api">Framework API</a>
- <ol>
- <li><a href="#api-level">API level</a></li>
- <li><a href="#api-changes">API changes summary</a></li>
- <li><a
-href="{@docRoot}sdk/api_diff/{@sdkPlatformApiLevel}/changes.html">API
-differences report »</a> </li>
+
</ol>
</li>
</ol>
+<h2>Reference</h2>
+<ol>
+<li><a
+href="{@docRoot}sdk/api_diff/{@sdkPlatformApiLevel}/changes.html">API
+Differences Report »</a> </li>
+</ol>
+
<h2>See Also</h2>
<ol>
<li><a href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a></li>
@@ -91,7 +94,8 @@
</script>
<style>
.toggleable {
-padding: .25em 1em;
+ padding: .25em 1em 0em 1em;
+ margin-bottom: 0;
}
.toggleme {
padding: 1em 1em 0 2em;
@@ -131,6 +135,201 @@
</div>
</div>
+
+<h2 id="api-level">API Level</h2>
+
+<p>The Android {@sdkPlatformVersion} platform delivers an updated version of
+the framework API. The Android {@sdkPlatformVersion} API
+is assigned an integer identifier —
+<strong>{@sdkPlatformApiLevel}</strong> — that is
+stored in the system itself. This identifier, called the "API Level", allows the
+system to correctly determine whether an application is compatible with
+the system, prior to installing the application. </p>
+
+<p>To use APIs introduced in Android {@sdkPlatformVersion} in your
+application, you need to set the proper value, "{@sdkPlatformApiLevel}", in the
+<code>android:minSdkVersion</code> attributes of the <code><uses-sdk></code>
+element in your application's manifest. </p>
+
+<p>For more information about how to use API Level, see the <a
+href="{@docRoot}guide/appendix/api-levels.html">API Levels</a> document. </p>
+
+
+<h2 id="api">Framework API Changes</h2>
+
+<p>The sections below provide information about changes made to the application
+framework API provided by the Android {@sdkPlatformVersion} platform.</p>
+
+<h3 id="install-loc">App installation on external storage media</h3>
+
+<p>The Android platform now allows applications to request installation onto the
+device's external storage media (such as the SD card), as an alternative to
+installation onto the device's internal memory. </p>
+
+<p>Application developers can express the preferred installation location for
+their applications by means of a new attribute of <code><manifest></code>
+in the manifest file, <a
+href="{@docRoot}guide/topics/manifest/manifest-element.html#install"><code>
+android:installLocation</code></a>. The attribute supports three values:
+<code>"internalOnly"</code>, <code>"preferExternal"</code>, and
+<code>"auto"</code>. At install time, the system checks the value of
+<code>android:installLocation</code> and installs the application
+<code>.apk</code> according to the preferred location, if possible. If the
+application has requested external installation, the system installs it into a
+private, encrypted partition in the external media. Once an application .apk is
+installed externally, the system lets the user change the storage location of
+the .apk and move it onto the device's internal memory if needed (and vice
+versa), through Manage Applications in the user settings.</p>
+
+<p>By default, the system installs all applications onto the device's internal
+memory, except for those that explicitly request external installation. This
+means that the system will always install legacy applications onto internal
+memory, since they do not have access to the
+<code>android:installLocation</code> attribute. However, it is possible to
+configure and compile a legacy application such that it is installed internally
+on older versions of the platform and externally on Android 2.2 and later
+platforms, if necessary. </p>
+
+<p>Note that requesting installation onto the device's external media is not
+suitable for all applications, particularly because the external media may be
+removable and unmounting/remounting may disrupt the user experience and system
+settings.</p>
+
+<p>For more information about setting a preferred install location for your
+application, including a discussion of what types of applications should and
+should not request external installation, please read the <a
+href="{@docRoot}guide/appendix/install-location.html">App Install Location</a>
+document. </p>
+
+<h3 id="backup-manager">Data backup</h3>
+
+<p>The platform now provides a generalized backup service that
+applications can use to backup and restore user data, to ensure that users can
+maintain their data when switching devices or reinstalling the application. The
+Backup Manager handles the work of transporting the application data to and from
+the backup storage area in the cloud. The Backup Manager can store any type of
+data, from arbitrary data to files, and manages backup and restore operations
+in an atomic manner. For more information, see <a
+href="{@docRoot}guide/topics/data/backup.html">Data Backup</a>.</p>
+
+<h3>Graphics</h3>
+
+<ul>
+<li>New OpenGL ES 2.0 APIs in {@link android.opengl.GLES20 android.opengl.GLES20}.</li>
+<li>New {@link android.opengl.ETC1}, {@link android.opengl.ETC1Util}, and {@link android.opengl.ETC1Util.ETC1Texture} classes and utility methods for using ETC1 for texture compression.</li>
+<li>New {@link android.graphics.ImageFormat} class.</li>
+<li>New {@link android.graphics.YuvImage YUV image format API} to enable compression from YUV to JPEG and manipulation of YUV data.</li>
+</ul>
+
+<h3>Media</h3>
+
+<ul>
+<li>New APIs in {@link android.media.AudioManager android.media.AudioManager} for managing audio focus, transport control, transient loss of audio focus, ducking.</li>
+<li>New broadcast intent for routing audio to SCO — {@link android.media.AudioManager#ACTION_SCO_AUDIO_STATE_CHANGED} with extras indicating new state.</li>
+<li>New APIs in {@link android.media.SoundPool} to detect completion of sound-loading.</li>
+<li>New APIs in {@link android.media.SoundPool} for auto pause and resume.</li>
+<li>New APIs in {@link android.media.MediaRecorder} for specifying audio settings for number of channels, encoding and sampling rates, sampling rate.</li>
+<li>New APIs for adding files to the media database, so that they are automatically scanned. See {@link android.media.MediaScannerConnection#scanFile(Context, String[], String[], OnScanCompletedListener) MediaScannerConnection.scanFile} and {@link android.media.MediaScannerConnection.OnScanCompletedListener MediaScannerConnection.OnScanCompletedListener}.</li>
+</ul>
+
+<h3>Speech recognition and third-party recognition engines</h3>
+
+<ul>
+<li>The platform provides new speech-recognition APIs that allow applications to have a richer interaction with the available voice recognizer. For example, the APIs are sufficient to integrate voice recognition deeply into an IME.</li>
+<li>The platform also provides a {@link android.speech.RecognitionService} base class that lets third-party developers create plug-in recognition engines. </li>
+<li>New {@link android.speech.RecognitionListener} interface to receive callbacks.</li>
+<li>New {@link android.speech.RecognizerIntent} extras that let a requester app specify details as preferred language, minimum length in milliseconds, and so on.</li>
+</ul>
+
+<h3>Camera and camcorder</h3>
+
+<ul>
+<li>Changes to camera preview API to improve efficieny of preview pipeline. </li>
+<li>New display orientation for camera (it can now work in portrait orientation).</li>
+<li>New APIs in {@link android.hardware.Camera android.hardware.Camera} for managing zoom level.</li>
+<li>New APIs {@link android.hardware.Camera.Parameters android.hardware.Camera.Parameters} for querying and setting device camera settings such as focal length, exposure, zoom level, view angle, and others.</li>
+<li>New {@link android.media.ThumbnailUtils thumbnail} utility for video and image thumbnails.</li>
+<li>New {@link android.media.CamcorderProfile} and {@link android.media.CamcorderProfile} classes enable apps to determine device hardware camera capablities.</li>
+<li>New support in {@link android.media.ExifInterface android.media.ExifInterface} for retrieving GPS and focal length.</li>
+</ul>
+
+<h3>Device policy manager</h3>
+
+<p>New device policy management APIs allow developers to write "device
+administrator" applications that can control security features of the device,
+such as the minimum password strength, data wipe, and so on. Users can select
+the administrators that are enabled on their devices. For more information, see
+the {@link android.app.admin android.app.admin} classees or the example
+application code in <a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.html">DeviceAdminSample.java</a>.</p>
+
+<h3>UI Framework</h3>
+
+<ul>
+<li>New UI modes "car mode" and "night mode" and {@link android.app.UiModeManager} let applications adjust their application UI for specific user modes. </li>
+<li>New {@link android.view.ScaleGestureDetector} that lets Views detect and handle transformation gestures that involve more than one pointer (multitouch) using the supplied MotionEvents. </li>
+<li>Improvements in the way that multitouch events are reported in {@link android.view.MotionEvent} objects.</li>
+<li>The layout attribute <code>fill_parent</code> is renamed to <code>match_parent</code>. This affects both XML and Java code (see {@link android.view.ViewGroup.LayoutParams}). Note that the platform will continue to honor uses of <code>fill_parent</code> in legacy applications. </li>
+<li>New layout attributes {@link android.R.attr#tabStripEnabled}, {@link android.R.attr#tabStripRight}, and {@link android.R.attr#tabStripLeft} let developers customize the bottom strip of TabWidgets.</li>
+<li>Better support for managed dialogs in Activity.</li>
+</ul>
+
+<h3>Accounts and sync</h3>
+
+<ul>
+<li>New method {@link android.content.ContentResolver#addPeriodicSync(Account, String, Bundle, long) AddPeriodicSync()} lets you schedule a periodic sync with a specific account, authority, and extras at the given frequency.</li>
+</ul>
+
+<h3>New manifest elements and attributes</h3>
+
+<ul>
+<li>For specifying the application's preferred install location (see <a href="#install-loc">App Installation on External Storage Media</a>, above):
+
+<ul>
+ <li>New <code>android:installLocation</code> attribute of the <code><manifest></code> element. Specifies the default install location defined by an application.</li>
+</ul>
+</li>
+
+<li>For managing user data backup (see <a href="#backup-manager">Backup manager</a>, above, for more information):
+
+<ul>
+ <li> New <code>android:backupAgent</code> attribute of the
+<code><application></code> element. Specifies the component name of the
+BackupAgent subclass provided by the application to handle backup/restore
+operations, if any.</li>
+ <li> New <code>android:restoreAnyVersion</code> attribute of the
+<code><application></code> element. Boolean value that indicates whether
+the application is prepared to attempt a restore of any backed-up dataset, even
+if the backup is apparently from a newer version of the application than is
+currently installed on the device.</li>
+</ul>
+</li>
+
+<li>For managing the platform's JIT compiler:
+
+<ul>
+<li>New <code>android:vmSafeMode</code> attribute of the <code><application></code> element. Boolean value that specifies whether to disable JIT compiler optimizations when running the application.</li>
+</ul>
+</li>
+</ul>
+
+<h3>Permissions</h3>
+
+<ul>
+<li><code>android.permission.BIND_DEVICE_ADMIN</code> — Any device administration broadcast receiver must require this permission, to ensure that only the system can interact with it.</li>
+<li><code>android.permission.KILL_BACKGROUND_PROCESSES</code> — Allows an application to call {@link android.app.ActivityManager#killBackgroundProcesses(String)}.
+<li><code>android.permission.BIND_WALLPAPER</code> — Any {@link android.service.wallpaper.WallpaperService} must require this permission, to ensure that only the system can interact with it.</li>
+<li><code>android.permission.SET_TIME</code> — Allows an application to set the system time.</li>
+</ul>
+
+<h3 id="api-diff">API differences report</h3>
+
+<p>For a detailed view of all API changes in Android {@sdkPlatformVersion} (API
+Level {@sdkPlatformApiLevel}), see the <a
+href="{@docRoot}sdk/api_diff/{@sdkPlatformApiLevel}/changes.html">API
+Differences Report</a>.</p>
+
+
<h2 id="apps">Built-in Applications</h2>
<p>The system image included in the downloadable platform provides these
@@ -245,210 +444,4 @@
<p>For more information about how to develop an application that displays
and functions properly on all Android-powered devices, see <a
href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple
-Screens</a>.</p>
-
-<h2 id="api">Framework API</h2>
-
-<p>The sections below provide information about changes made to the application
-framework API provided by the Android {@sdkPlatformVersion} platform.</p>
-
-
-<h3 id="api-level">API level</h3>
-
-<p>The Android {@sdkPlatformVersion} platform delivers an updated version of
-the framework API. The Android {@sdkPlatformVersion} API
-is assigned an integer identifier —
-<strong>{@sdkPlatformApiLevel}</strong> — that is
-stored in the system itself. This identifier, called the "API Level", allows the
-system to correctly determine whether an application is compatible with
-the system, prior to installing the application. </p>
-
-<p>To use APIs introduced in Android {@sdkPlatformVersion} in your
-application, you need to set the proper value, "{@sdkPlatformApiLevel}", in the
-<code>android:minSdkVersion</code> attributes of the <code><uses-sdk></code>
-element in your application's manifest. </p>
-
-<p>For more information about how to use API Level, see the <a
-href="{@docRoot}guide/appendix/api-levels.html">API Levels</a> document. </p>
-
-
-<h3 id="api-changes">API changes summary</h3>
-
-<p>The sections below summarize the changes to the application framework and its APIs.</p>
-
-<h4 id="install-loc">App Installation on External Storage Media</h4>
-
-<p>The Android platform now allows applications to request installation onto the
-device's external storage media (such as the SD card), as an alternative to
-installation onto the device's internal memory. </p>
-
-<p>Application developers can express the preferred installation location for
-their applications by means of a new attribute of <code><manifest></code>
-in the manifest file, <a
-href="{@docRoot}guide/topics/manifest/manifest-element.html#install"><code>
-android:installLocation</code></a>. The attribute supports three values:
-<code>"internalOnly"</code>, <code>"preferExternal"</code>, and
-<code>"auto"</code>. At install time, the system checks the value of
-<code>android:installLocation</code> and installs the application
-<code>.apk</code> according to the preferred location, if possible. If the
-application has requested external installation, the system installs it into a
-private, encrypted partition in the external media. Once an application .apk is
-installed externally, the system lets the user change the storage location of
-the .apk and move it onto the device's internal memory if needed (and vice
-versa), through Manage Applications in the user settings.</p>
-
-<p>By default, the system installs all applications onto the device's internal
-memory, except for those that explicitly request external installation. This
-means that the system will always install legacy applications onto internal
-memory, since they do not have access to the
-<code>android:installLocation</code> attribute. However, it is possible to
-configure and compile a legacy application such that it is installed internally
-on older versions of the platform and externally on Android 2.2 and later
-platforms, if necessary. </p>
-
-<p>Note that requesting installation onto the device's external media is not
-suitable for all applications, particularly because the external media may be
-removable and unmounting/remounting may disrupt the user experience and system
-settings.</p>
-
-<p>For more information about setting a preferred install location for your
-application, including a discussion of what types of applications should and
-should not request external installation, please read the <a
-href="{@docRoot}guide/appendix/install-location.html">App Install Location</a>
-document. </p>
-
-<h4 id="backup-manager">Backup manager</h4>
-
-<p>The platform now provides a generalized backup service that
-applications can use to backup and restore user data, to ensure that users can
-maintain their data when switching devices or reinstalling the application. The
-Backup Manager handles the work of transporting the application data to and from
-the backup storage area in the cloud. The Backup Manager can store any type of
-data, from arbitrary data to files, and manages backup and restore operations
-in an atomic manner. For more information, see <a
-href="{@docRoot}guide/topics/data/backup.html">Data Backup</a>.</p>
-
-<h4>Graphics</h4>
-
-<ul>
-<li>New OpenGL ES 2.0 APIs in {@link android.opengl.GLES20 android.opengl.GLES20}.</li>
-<li>New {@link android.opengl.ETC1}, {@link android.opengl.ETC1Util}, and {@link android.opengl.ETC1Util.ETC1Texture} classes and utility methods for using ETC1 for texture compression.</li>
-<li>New {@link android.graphics.ImageFormat} class.</li>
-<li>New {@link android.graphics.YuvImage YUV image format API} to enable compression from YUV to JPEG and manipulation of YUV data.</li>
-</ul>
-
-<h4>Media</h4>
-
-<ul>
-<li>New APIs in {@link android.media.AudioManager android.media.AudioManager} for managing audio focus, transport control, transient loss of audio focus, ducking.</li>
-<li>New broadcast intent for routing audio to SCO — {@link android.media.AudioManager#ACTION_SCO_AUDIO_STATE_CHANGED} with extras indicating new state.</li>
-<li>New APIs in {@link android.media.SoundPool} to detect completion of sound-loading.</li>
-<li>New APIs in {@link android.media.SoundPool} for auto pause and resume.</li>
-<li>New APIs in {@link android.media.MediaRecorder} for specifying audio settings for number of channels, encoding and sampling rates, sampling rate.</li>
-<li>New APIs for adding files to the media database, so that they are automatically scanned. See {@link android.media.MediaScannerConnection#scanFile(Context, String[], String[], OnScanCompletedListener) MediaScannerConnection.scanFile} and {@link android.media.MediaScannerConnection.OnScanCompletedListener MediaScannerConnection.OnScanCompletedListener}.</li>
-</ul>
-
-<h4>Speech recognition and third-party recognition engines</h4>
-
-<ul>
-<li>The platform provides new speech-recognition APIs that allow applications to have a richer interaction with the available voice recognizer. For example, the APIs are sufficient to integrate voice recognition deeply into an IME.</li>
-<li>The platform also provides a {@link android.speech.RecognitionService} base class that lets third-party developers create plug-in recognition engines. </li>
-<li>New {@link android.speech.RecognitionListener} interface to receive callbacks.</li>
-<li>New {@link android.speech.RecognizerIntent} extras that let a requester app specify details as preferred language, minimum length in milliseconds, and so on.</li>
-</ul>
-
-<h4>Camera and camcorder</h4>
-
-<ul>
-<li>Changes to camera preview API to improve efficieny of preview pipeline. </li>
-<li>New display orientation for camera (it can now work in portrait orientation).</li>
-<li>New APIs in {@link android.hardware.Camera android.hardware.Camera} for managing zoom level.</li>
-<li>New APIs {@link android.hardware.Camera.Parameters android.hardware.Camera.Parameters} for querying and setting device camera settings such as focal length, exposure, zoom level, view angle, and others.</li>
-<li>New {@link android.media.ThumbnailUtils thumbnail} utility for video and image thumbnails.</li>
-<li>New {@link android.media.CamcorderProfile} and {@link android.media.CamcorderProfile} classes enable apps to determine device hardware camera capablities.</li>
-<li>New support in {@link android.media.ExifInterface android.media.ExifInterface} for retrieving GPS and focal length.</li>
-</ul>
-
-<h4>Device policy manager</h4>
-
-<p>New device policy management APIs allow developers to write "device
-administrator" applications that can control security features of the device,
-such as the minimum password strength, data wipe, and so on. Users can select
-the administrators that are enabled on their devices. For more information, see
-the {@link android.app.admin android.app.admin} classees or the example
-application code in <a
-href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.html">DeviceAdminSample.java</a>.</p>
-
-<h4>UI Framework</h4>
-
-<ul>
-<li>New UI modes "car mode" and "night mode" and {@link android.app.UiModeManager} let applications adjust their application UI for specific user modes. </li>
-<li>New {@link android.view.ScaleGestureDetector} that lets Views detect and handle transformation gestures that involve more than one pointer (multitouch) using the supplied MotionEvents. </li>
-<li>Improvements in the way that multitouch events are reported in {@link android.view.MotionEvent} objects.</li>
-<li>The layout attribute <code>fill_parent</code> is renamed to <code>match_parent</code>. This affects both XML and Java code (see {@link android.view.ViewGroup.LayoutParams}). Note that the platform will continue to honor uses of <code>fill_parent</code> in legacy applications. </li>
-<li>New layout attributes {@link android.R.attr#tabStripEnabled}, {@link android.R.attr#tabStripRight}, and {@link android.R.attr#tabStripLeft} let developers customize the bottom strip of TabWidgets.</li>
-<li>Better support for managed dialogs in Activity.</li>
-</ul>
-
-<h4>Accounts and Sync</h4>
-
-<ul>
-<li>New method {@link android.content.ContentResolver#addPeriodicSync(Account, String, Bundle, long) AddPeriodicSync()} lets you schedule a periodic sync with a specific account, authority, and extras at the given frequency.</li>
-</ul>
-
-<h4>New manifest elements and attributes</h4>
-
-<ul>
-<li>For specifying the application's preferred install location (see <a href="#install-loc">App Installation on External Storage Media</a>, above):
-
-<ul>
- <li>New <code>android:installLocation</code> attribute of the <code><manifest></code> element. Specifies the default install location defined by an application.</li>
-</ul>
-</li>
-
-<li>For managing user data backup (see <a href="#backup-manager">Backup manager</a>, above, for more information):
-
-<ul>
- <li> New <code>android:backupAgent</code> attribute of the
-<code><application></code> element. Specifies the component name of the
-BackupAgent subclass provided by the application to handle backup/restore
-operations, if any.</li>
- <li> New <code>android:restoreAnyVersion</code> attribute of the
-<code><application></code> element. Boolean value that indicates whether
-the application is prepared to attempt a restore of any backed-up dataset, even
-if the backup is apparently from a newer version of the application than is
-currently installed on the device.</li>
-</ul>
-</li>
-
-<li>For managing the platform's JIT compiler:
-
-<ul>
-<li>New <code>android:vmSafeMode</code> attribute of the <code><application></code> element. Boolean value that specifies whether to disable JIT compiler optimizations when running the application.</li>
-</ul>
-</li>
-</ul>
-
-<h4>Permissions</h4>
-
-<ul>
-<li><code>android.permission.BIND_DEVICE_ADMIN</code> — Any device administration broadcast receiver must require this permission, to ensure that only the system can interact with it.</li>
-<li><code>android.permission.KILL_BACKGROUND_PROCESSES</code> — Allows an application to call {@link android.app.ActivityManager#killBackgroundProcesses(String)}.
-<li><code>android.permission.BIND_WALLPAPER</code> — Any {@link android.service.wallpaper.WallpaperService} must require this permission, to ensure that only the system can interact with it.</li>
-<li><code>android.permission.SET_TIME</code> — Allows an application to set the system time.</li>
-</ul>
-
-<!--
-<h3 id="behavior-changes">Behavior changes</h3>
-
-What did change is that android:process and android:sharedUserId can now be a reference to a resource (instead of just a literal).
-<h3 id="bug-fixes">Bug fixes</h3>
--->
-
-<h3 id="api-diff">API differences report</h3>
-
-<p>For a detailed view of all API changes in Android {@sdkPlatformVersion} (API
-Level {@sdkPlatformApiLevel}), see the <a
-href="{@docRoot}sdk/api_diff/{@sdkPlatformApiLevel}/changes.html">API
-Differences Report</a>.</p>
-
+Screens</a>.</p>
\ No newline at end of file
diff --git a/docs/html/sdk/images/2.2/22browser.png b/docs/html/sdk/images/2.2/22browser.png
new file mode 100644
index 0000000..817439d
--- /dev/null
+++ b/docs/html/sdk/images/2.2/22browser.png
Binary files differ
diff --git a/docs/html/sdk/images/2.2/22exchange.png b/docs/html/sdk/images/2.2/22exchange.png
new file mode 100644
index 0000000..1fa1f59
--- /dev/null
+++ b/docs/html/sdk/images/2.2/22exchange.png
Binary files differ
diff --git a/docs/html/sdk/images/2.2/22gallery.png b/docs/html/sdk/images/2.2/22gallery.png
new file mode 100644
index 0000000..0cb74ad
--- /dev/null
+++ b/docs/html/sdk/images/2.2/22gallery.png
Binary files differ
diff --git a/docs/html/sdk/images/2.2/22home.png b/docs/html/sdk/images/2.2/22home.png
new file mode 100644
index 0000000..a11ea30
--- /dev/null
+++ b/docs/html/sdk/images/2.2/22home.png
Binary files differ
diff --git a/docs/html/sdk/images/2.2/22hotspot.png b/docs/html/sdk/images/2.2/22hotspot.png
new file mode 100644
index 0000000..0951439
--- /dev/null
+++ b/docs/html/sdk/images/2.2/22hotspot.png
Binary files differ
diff --git a/docs/html/sdk/images/2.2/22keyboard.png b/docs/html/sdk/images/2.2/22keyboard.png
new file mode 100644
index 0000000..69f95ca
--- /dev/null
+++ b/docs/html/sdk/images/2.2/22keyboard.png
Binary files differ
diff --git a/docs/html/sdk/images/2.2/jit-graph.png b/docs/html/sdk/images/2.2/jit-graph.png
new file mode 100644
index 0000000..52b8d60
--- /dev/null
+++ b/docs/html/sdk/images/2.2/jit-graph.png
Binary files differ
diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h
index 207195a..a9a0d55 100644
--- a/include/media/stagefright/MediaDefs.h
+++ b/include/media/stagefright/MediaDefs.h
@@ -22,6 +22,7 @@
extern const char *MEDIA_MIMETYPE_IMAGE_JPEG;
+extern const char *MEDIA_MIMETYPE_VIDEO_VPX;
extern const char *MEDIA_MIMETYPE_VIDEO_AVC;
extern const char *MEDIA_MIMETYPE_VIDEO_MPEG4;
extern const char *MEDIA_MIMETYPE_VIDEO_H263;
@@ -38,6 +39,7 @@
extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4;
extern const char *MEDIA_MIMETYPE_CONTAINER_WAV;
extern const char *MEDIA_MIMETYPE_CONTAINER_OGG;
+extern const char *MEDIA_MIMETYPE_CONTAINER_MATROSKA;
} // namespace android
diff --git a/location/java/android/location/Criteria.java b/location/java/android/location/Criteria.java
index 9d258d0..647a9df 100644
--- a/location/java/android/location/Criteria.java
+++ b/location/java/android/location/Criteria.java
@@ -57,9 +57,72 @@
*/
public static final int ACCURACY_COARSE = 2;
- private int mAccuracy = NO_REQUIREMENT;
+ /**
+ * A constant indicating a low location accuracy requirement
+ * - may be used for horizontal, altitude, speed or bearing accuracy.
+ * For horizontal and vertical position this corresponds to an accuracy
+ * greater than 500 meters. For speed and bearing, this corresponds
+ * to greater than 5 meters/second velocity and 10 degrees for bearing.
+ */
+ public static final int ACCURACY_LOW = 1;
+
+ /**
+ * A constant indicating a medium accuracy requirement
+ * - may be used for horizontal, altitude, speed or bearing accuracy.
+ * For horizontal position this corresponds to an accuracy of between
+ * 100 and 500 meters, and between 200 and 500 meters for vertical accuracy.
+ * For speed and bearing, this corresponds to 1 meter/second to 5 meters/second
+ * velocity and and between 5 and 10 degrees for bearing.
+ */
+ public static final int ACCURACY_MEDIUM = 2;
+
+ /**
+ * a constant indicating a high accuracy requirement
+ * - may be used for horizontal, altitude, speed or bearing accuracy.
+ * For horizontal and vertical position this corresponds to an accuracy
+ * less than 100 meters. For speed and bearing, this corresponds
+ * to less 1 meter/second velocity less than 5 degrees for bearing.
+ */
+ public static final int ACCURACY_HIGH = 3;
+
+ /**
+ * a constant indicating the best accuracy that is available for any
+ * location provider available
+ * - may be used for horizontal, altitude, speed or bearing accuracy.
+ */
+ public static final int ACCURACY_BEST = 4;
+
+ /**
+ * A constant indicating horizontal accuracy has the top priority
+ */
+ public static final int HORIZONTAL_ACCURACY_PRIORITY = 1;
+
+ /**
+ * A constant indicating altitude accuracy has the top priority
+ */
+ public static final int VERTICAL_ACCURACY_PRIORITY = 2;
+
+ /**
+ * A constant indicating speed accuracy has the top priority
+ */
+ public static final int SPEED_ACCURACY_PRIORITY = 3;
+
+ /**
+ * A constant indicating bearing accuracy has the top priority
+ */
+ public static final int BEARING_ACCURACY_PRIORITY = 4;
+
+ /**
+ * A constant indicating power requirement has the top priority
+ */
+ public static final int POWER_REQUIREMENT_PRIORITY = 5;
+
+ private int mHorizontalAccuracy = NO_REQUIREMENT;
+ private int mVerticalAccuracy = NO_REQUIREMENT;
+ private int mSpeedAccuracy = NO_REQUIREMENT;
+ private int mBearingAccuracy = NO_REQUIREMENT;
+ private int mPriority = HORIZONTAL_ACCURACY_PRIORITY;
private int mPowerRequirement = NO_REQUIREMENT;
-// private int mPreferredResponseTime = NO_REQUIREMENT;
private boolean mAltitudeRequired = false;
private boolean mBearingRequired = false;
private boolean mSpeedRequired = false;
@@ -77,9 +140,12 @@
* Constructs a new Criteria object that is a copy of the given criteria.
*/
public Criteria(Criteria criteria) {
- mAccuracy = criteria.mAccuracy;
+ mHorizontalAccuracy = criteria.mHorizontalAccuracy;
+ mVerticalAccuracy = criteria.mVerticalAccuracy;
+ mSpeedAccuracy = criteria.mSpeedAccuracy;
+ mBearingAccuracy = criteria.mBearingAccuracy;
+ mPriority = criteria.mPriority;
mPowerRequirement = criteria.mPowerRequirement;
-// mPreferredResponseTime = criteria.mPreferredResponseTime;
mAltitudeRequired = criteria.mAltitudeRequired;
mBearingRequired = criteria.mBearingRequired;
mSpeedRequired = criteria.mSpeedRequired;
@@ -87,19 +153,149 @@
}
/**
+ * Indicates the desired horizontal accuracy (latitude and longitude).
+ * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM},
+ * {@link #ACCURACY_HIGH}, {@link #ACCURACY_BEST},
+ * More accurate location may consume more power and may take longer.
+ *
+ * @throws IllegalArgumentException if accuracy is not one of the supported constants
+ */
+ public void setHorizontalAccuracy(int accuracy) {
+ if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_BEST) {
+ throw new IllegalArgumentException("accuracy=" + accuracy);
+ }
+ mHorizontalAccuracy = accuracy;
+ }
+
+ /**
+ * Returns a constant indicating the desired horizontal accuracy (latitude and longitude).
+ * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM},
+ * {@link #ACCURACY_HIGH}, {@link #ACCURACY_BEST},
+ */
+ public int getHorizontalAccuracy() {
+ return mHorizontalAccuracy;
+ }
+
+ /**
+ * Indicates the desired vertical accuracy (altitude).
+ * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM},
+ * {@link #ACCURACY_HIGH}, {@link #ACCURACY_BEST},
+ * More accurate location may consume more power and may take longer.
+ *
+ * @throws IllegalArgumentException if accuracy is not one of the supported constants
+ */
+ public void setVerticalAccuracy(int accuracy) {
+ if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_BEST) {
+ throw new IllegalArgumentException("accuracy=" + accuracy);
+ }
+ mVerticalAccuracy = accuracy;
+ }
+
+ /**
+ * Returns a constant indicating the desired vertical accuracy (altitude).
+ * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM},
+ * {@link #ACCURACY_HIGH}, {@link #ACCURACY_BEST},
+ */
+ public int getVerticalAccuracy() {
+ return mVerticalAccuracy;
+ }
+
+ /**
+ * Indicates the desired speed accuracy.
+ * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM},
+ * {@link #ACCURACY_HIGH}, {@link #ACCURACY_BEST},
+ * More accurate location may consume more power and may take longer.
+ *
+ * @throws IllegalArgumentException if accuracy is not one of the supported constants
+ */
+ public void setSpeedAccuracy(int accuracy) {
+ if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_BEST) {
+ throw new IllegalArgumentException("accuracy=" + accuracy);
+ }
+ mSpeedAccuracy = accuracy;
+ }
+
+ /**
+ * Returns a constant indicating the desired speed accuracy
+ * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM},
+ * {@link #ACCURACY_HIGH}, {@link #ACCURACY_BEST},
+ */
+ public int getSpeedAccuracy() {
+ return mSpeedAccuracy;
+ }
+
+ /**
+ * Indicates the desired bearing accuracy.
+ * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM},
+ * {@link #ACCURACY_HIGH}, {@link #ACCURACY_BEST},
+ * More accurate location may consume more power and may take longer.
+ *
+ * @throws IllegalArgumentException if accuracy is not one of the supported constants
+ */
+ public void setBearingAccuracy(int accuracy) {
+ if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_BEST) {
+ throw new IllegalArgumentException("accuracy=" + accuracy);
+ }
+ mBearingAccuracy = accuracy;
+ }
+
+ /**
+ * Returns a constant indicating the desired bearing accuracy.
+ * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM},
+ * {@link #ACCURACY_HIGH}, {@link #ACCURACY_BEST},
+ */
+ public int getBearingAccuracy() {
+ return mBearingAccuracy;
+ }
+
+ /**
+ * Indicates the top priority to optimize for if the criteria parameters are
+ * found to be in conflict.
+ * Since a location provider might only be able to optimize for one requirement,
+ * the other requirements are considered good to have, but not guaranteed.
+ * This parameter does not override the priorities communicated through the
+ * preferred accuracy and power consumption parameters.
+ * If this parameter is not specified and conflicts occur, the location manager
+ * will use thefollowing default priority (high priority to low priority):
+ * {@link #HORIZONTAL_ACCURACY_PRIORITY}, {@link #POWER_REQUIREMENT_PRIORITY},
+ * {@link #VERTICAL_ACCURACY_PRIORITY}, {@link #SPEED_ACCURACY_PRIORITY},
+ * {@link #BEARING_ACCURACY_PRIORITY}.
+ */
+ public void setPreferredPriority(int priority) {
+ if (priority < HORIZONTAL_ACCURACY_PRIORITY || priority > POWER_REQUIREMENT_PRIORITY) {
+ throw new IllegalArgumentException("priority=" + priority);
+ }
+ mPriority = priority;
+ }
+
+ /**
+ * Returns a constant indicating the top priority to optimize for if the
+ * criteria parameters are found to be in conflict.
+ * The value can be {@link #HORIZONTAL_ACCURACY_PRIORITY},
+ * {@link #VERTICAL_ACCURACY_PRIORITY}, {@link #SPEED_ACCURACY_PRIORITY},
+ * {@link #BEARING_ACCURACY_PRIORITY} or {@link #POWER_REQUIREMENT_PRIORITY}.
+ */
+ public int getPriority() {
+ return mPriority;
+ }
+
+ /**
* Indicates the desired accuracy for latitude and longitude. Accuracy
* may be {@link #ACCURACY_FINE} if desired location
* is fine, else it can be {@link #ACCURACY_COARSE}.
- * More accurate location usually consumes more power and may take
- * longer.
+ * More accurate location may consume more power and may take longer.
*
- * @throws IllegalArgumentException if accuracy is negative
+ * @throws IllegalArgumentException if accuracy is not one of the supported constants
*/
public void setAccuracy(int accuracy) {
- if (accuracy < NO_REQUIREMENT && accuracy > ACCURACY_COARSE) {
+ if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_COARSE) {
throw new IllegalArgumentException("accuracy=" + accuracy);
}
- mAccuracy = accuracy;
+ if (accuracy == ACCURACY_FINE) {
+ mHorizontalAccuracy = ACCURACY_BEST;
+ } else {
+ mHorizontalAccuracy = ACCURACY_LOW;
+ }
}
/**
@@ -108,7 +304,11 @@
* is fine, else it can be {@link #ACCURACY_COARSE}.
*/
public int getAccuracy() {
- return mAccuracy;
+ if (mHorizontalAccuracy >= ACCURACY_HIGH) {
+ return ACCURACY_FINE;
+ } else {
+ return ACCURACY_COARSE;
+ }
}
/**
@@ -131,20 +331,6 @@
return mPowerRequirement;
}
-// /**
-// * Indicates the preferred response time of the provider, in milliseconds.
-// */
-// public void setPreferredResponseTime(int time) {
-// mPreferredResponseTime = time;
-// }
-//
-// /**
-// * Returns the preferred response time of the provider, in milliseconds.
-// */
-// public int getPreferredResponseTime() {
-// return mPreferredResponseTime;
-// }
-
/**
* Indicates whether the provider is allowed to incur monetary cost.
*/
@@ -211,9 +397,12 @@
new Parcelable.Creator<Criteria>() {
public Criteria createFromParcel(Parcel in) {
Criteria c = new Criteria();
- c.mAccuracy = in.readInt();
+ c.mHorizontalAccuracy = in.readInt();
+ c.mVerticalAccuracy = in.readInt();
+ c.mSpeedAccuracy = in.readInt();
+ c.mBearingAccuracy = in.readInt();
+ c.mPriority = in.readInt();
c.mPowerRequirement = in.readInt();
-// c.mPreferredResponseTime = in.readInt();
c.mAltitudeRequired = in.readInt() != 0;
c.mBearingRequired = in.readInt() != 0;
c.mSpeedRequired = in.readInt() != 0;
@@ -231,9 +420,12 @@
}
public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeInt(mAccuracy);
+ parcel.writeInt(mHorizontalAccuracy);
+ parcel.writeInt(mVerticalAccuracy);
+ parcel.writeInt(mSpeedAccuracy);
+ parcel.writeInt(mBearingAccuracy);
+ parcel.writeInt(mPriority);
parcel.writeInt(mPowerRequirement);
-// parcel.writeInt(mPreferredResponseTime);
parcel.writeInt(mAltitudeRequired ? 1 : 0);
parcel.writeInt(mBearingRequired ? 1 : 0);
parcel.writeInt(mSpeedRequired ? 1 : 0);
diff --git a/location/java/android/location/GeocoderParams.java b/location/java/android/location/GeocoderParams.java
index 174fe3e..8b8e63b 100644
--- a/location/java/android/location/GeocoderParams.java
+++ b/location/java/android/location/GeocoderParams.java
@@ -29,8 +29,6 @@
* as well as the Geocoder client's package name for geocoder server
* logging. This information is kept in a separate class to allow for
* future expansion of the IGeocodeProvider interface.
- *
- * @hide
*/
public class GeocoderParams implements Parcelable {
private Locale mLocale;
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 2c0399e..a86f329 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -18,6 +18,7 @@
import android.app.PendingIntent;
import android.location.Address;
+import android.location.Criteria;
import android.location.GeocoderParams;
import android.location.IGeocodeProvider;
import android.location.IGpsStatusListener;
@@ -32,13 +33,15 @@
*/
interface ILocationManager
{
- List getAllProviders();
- List getProviders(boolean enabledOnly);
+ List<String> getAllProviders();
+ List<String> getProviders(in Criteria criteria, boolean enabledOnly);
+ String getBestProvider(in Criteria criteria, boolean enabledOnly);
+ boolean providerMeetsCriteria(String provider, in Criteria criteria);
- void requestLocationUpdates(String provider, long minTime, float minDistance,
- in ILocationListener listener);
- void requestLocationUpdatesPI(String provider, long minTime, float minDistance,
- in PendingIntent intent);
+ void requestLocationUpdates(String provider, in Criteria criteria, long minTime, float minDistance,
+ boolean singleShot, in ILocationListener listener);
+ void requestLocationUpdatesPI(String provider, in Criteria criteria, long minTime, float minDistance,
+ boolean singleShot, in PendingIntent intent);
void removeUpdates(in ILocationListener listener);
void removeUpdatesPI(in PendingIntent intent);
diff --git a/location/java/android/location/ILocationProvider.aidl b/location/java/android/location/ILocationProvider.aidl
index 97b283c..2b9782a 100644
--- a/location/java/android/location/ILocationProvider.aidl
+++ b/location/java/android/location/ILocationProvider.aidl
@@ -16,6 +16,7 @@
package android.location;
+import android.location.Criteria;
import android.location.Location;
import android.net.NetworkInfo;
import android.os.Bundle;
@@ -34,6 +35,7 @@
boolean supportsSpeed();
boolean supportsBearing();
int getPowerRequirement();
+ boolean meetsCriteria(in Criteria criteria);
int getAccuracy();
void enable();
void disable();
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 28bc599..7d07e4b 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -28,8 +28,6 @@
import com.android.internal.location.DummyLocationProvider;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
@@ -249,15 +247,12 @@
* factory Context.getSystemService.
*/
public LocationManager(ILocationManager service) {
- if (false) {
- Log.d(TAG, "Constructor: service = " + service);
- }
mService = service;
}
private LocationProvider createProvider(String name, Bundle info) {
DummyLocationProvider provider =
- new DummyLocationProvider(name);
+ new DummyLocationProvider(name, mService);
provider.setRequiresNetwork(info.getBoolean("network"));
provider.setRequiresSatellite(info.getBoolean("satellite"));
provider.setRequiresCell(info.getBoolean("cell"));
@@ -299,7 +294,7 @@
*/
public List<String> getProviders(boolean enabledOnly) {
try {
- return mService.getProviders(enabledOnly);
+ return mService.getProviders(null, enabledOnly);
} catch (RemoteException ex) {
Log.e(TAG, "getProviders: RemoteException", ex);
}
@@ -344,173 +339,15 @@
* @return list of Strings containing names of the providers
*/
public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
- List<String> goodProviders = Collections.emptyList();
- List<String> providers = getProviders(enabledOnly);
- for (String providerName : providers) {
- LocationProvider provider = getProvider(providerName);
- if (provider != null && provider.meetsCriteria(criteria)) {
- if (goodProviders.isEmpty()) {
- goodProviders = new ArrayList<String>();
- }
- goodProviders.add(providerName);
- }
+ if (criteria == null) {
+ throw new IllegalArgumentException("criteria==null");
}
- return goodProviders;
- }
-
- /**
- * Returns the next looser power requirement, in the sequence:
- *
- * POWER_LOW -> POWER_MEDIUM -> POWER_HIGH -> NO_REQUIREMENT
- */
- private int nextPower(int power) {
- switch (power) {
- case Criteria.POWER_LOW:
- return Criteria.POWER_MEDIUM;
- case Criteria.POWER_MEDIUM:
- return Criteria.POWER_HIGH;
- case Criteria.POWER_HIGH:
- return Criteria.NO_REQUIREMENT;
- case Criteria.NO_REQUIREMENT:
- default:
- return Criteria.NO_REQUIREMENT;
+ try {
+ return mService.getProviders(criteria, enabledOnly);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "getProviders: RemoteException", ex);
}
- }
-
- /**
- * Returns the next looser accuracy requirement, in the sequence:
- *
- * ACCURACY_FINE -> ACCURACY_APPROXIMATE-> NO_REQUIREMENT
- */
- private int nextAccuracy(int accuracy) {
- if (accuracy == Criteria.ACCURACY_FINE) {
- return Criteria.ACCURACY_COARSE;
- } else {
- return Criteria.NO_REQUIREMENT;
- }
- }
-
- private abstract class LpComparator implements Comparator<LocationProvider> {
-
- public int compare(int a1, int a2) {
- if (a1 < a2) {
- return -1;
- } else if (a1 > a2) {
- return 1;
- } else {
- return 0;
- }
- }
-
- public int compare(float a1, float a2) {
- if (a1 < a2) {
- return -1;
- } else if (a1 > a2) {
- return 1;
- } else {
- return 0;
- }
- }
- }
-
- private class LpPowerComparator extends LpComparator {
- public int compare(LocationProvider l1, LocationProvider l2) {
- int a1 = l1.getPowerRequirement();
- int a2 = l2.getPowerRequirement();
- return compare(a1, a2); // Smaller is better
- }
-
- public boolean equals(LocationProvider l1, LocationProvider l2) {
- int a1 = l1.getPowerRequirement();
- int a2 = l2.getPowerRequirement();
- return a1 == a2;
- }
- }
-
- private class LpAccuracyComparator extends LpComparator {
- public int compare(LocationProvider l1, LocationProvider l2) {
- int a1 = l1.getAccuracy();
- int a2 = l2.getAccuracy();
- return compare(a1, a2); // Smaller is better
- }
-
- public boolean equals(LocationProvider l1, LocationProvider l2) {
- int a1 = l1.getAccuracy();
- int a2 = l2.getAccuracy();
- return a1 == a2;
- }
- }
-
- private class LpCapabilityComparator extends LpComparator {
-
- private static final int ALTITUDE_SCORE = 4;
- private static final int BEARING_SCORE = 4;
- private static final int SPEED_SCORE = 4;
-
- private int score(LocationProvider p) {
- return (p.supportsAltitude() ? ALTITUDE_SCORE : 0) +
- (p.supportsBearing() ? BEARING_SCORE : 0) +
- (p.supportsSpeed() ? SPEED_SCORE : 0);
- }
-
- public int compare(LocationProvider l1, LocationProvider l2) {
- int a1 = score(l1);
- int a2 = score(l2);
- return compare(-a1, -a2); // Bigger is better
- }
-
- public boolean equals(LocationProvider l1, LocationProvider l2) {
- int a1 = score(l1);
- int a2 = score(l2);
- return a1 == a2;
- }
- }
-
- private LocationProvider best(List<String> providerNames) {
- List<LocationProvider> providers = new ArrayList<LocationProvider>(providerNames.size());
- for (String name : providerNames) {
- providers.add(getProvider(name));
- }
-
- if (providers.size() < 2) {
- return providers.get(0);
- }
-
- // First, sort by power requirement
- Collections.sort(providers, new LpPowerComparator());
- int power = providers.get(0).getPowerRequirement();
- if (power < providers.get(1).getPowerRequirement()) {
- return providers.get(0);
- }
-
- int idx, size;
-
- List<LocationProvider> tmp = new ArrayList<LocationProvider>();
- idx = 0;
- size = providers.size();
- while ((idx < size) && (providers.get(idx).getPowerRequirement() == power)) {
- tmp.add(providers.get(idx));
- idx++;
- }
-
- // Next, sort by accuracy
- Collections.sort(tmp, new LpAccuracyComparator());
- int acc = tmp.get(0).getAccuracy();
- if (acc < tmp.get(1).getAccuracy()) {
- return tmp.get(0);
- }
-
- List<LocationProvider> tmp2 = new ArrayList<LocationProvider>();
- idx = 0;
- size = tmp.size();
- while ((idx < size) && (tmp.get(idx).getAccuracy() == acc)) {
- tmp2.add(tmp.get(idx));
- idx++;
- }
-
- // Finally, sort by capability "score"
- Collections.sort(tmp2, new LpCapabilityComparator());
- return tmp2.get(0);
+ return null;
}
/**
@@ -536,72 +373,14 @@
* @return name of the provider that best matches the requirements
*/
public String getBestProvider(Criteria criteria, boolean enabledOnly) {
- List<String> goodProviders = getProviders(criteria, enabledOnly);
- if (!goodProviders.isEmpty()) {
- return best(goodProviders).getName();
+ if (criteria == null) {
+ throw new IllegalArgumentException("criteria==null");
}
-
- // Make a copy of the criteria that we can modify
- criteria = new Criteria(criteria);
-
- // Loosen power requirement
- int power = criteria.getPowerRequirement();
- while (goodProviders.isEmpty() && (power != Criteria.NO_REQUIREMENT)) {
- power = nextPower(power);
- criteria.setPowerRequirement(power);
- goodProviders = getProviders(criteria, enabledOnly);
+ try {
+ return mService.getBestProvider(criteria, enabledOnly);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "getBestProvider: RemoteException", ex);
}
- if (!goodProviders.isEmpty()) {
- return best(goodProviders).getName();
- }
-
-// // Loosen response time requirement
-// int responseTime = criteria.getPreferredResponseTime();
-// while (goodProviders.isEmpty() &&
-// (responseTime != Criteria.NO_REQUIREMENT)) {
-// responseTime += 1000;
-// if (responseTime > 60000) {
-// responseTime = Criteria.NO_REQUIREMENT;
-// }
-// criteria.setPreferredResponseTime(responseTime);
-// goodProviders = getProviders(criteria);
-// }
-// if (!goodProviders.isEmpty()) {
-// return best(goodProviders);
-// }
-
- // Loosen accuracy requirement
- int accuracy = criteria.getAccuracy();
- while (goodProviders.isEmpty() && (accuracy != Criteria.NO_REQUIREMENT)) {
- accuracy = nextAccuracy(accuracy);
- criteria.setAccuracy(accuracy);
- goodProviders = getProviders(criteria, enabledOnly);
- }
- if (!goodProviders.isEmpty()) {
- return best(goodProviders).getName();
- }
-
- // Remove bearing requirement
- criteria.setBearingRequired(false);
- goodProviders = getProviders(criteria, enabledOnly);
- if (!goodProviders.isEmpty()) {
- return best(goodProviders).getName();
- }
-
- // Remove speed requirement
- criteria.setSpeedRequired(false);
- goodProviders = getProviders(criteria, enabledOnly);
- if (!goodProviders.isEmpty()) {
- return best(goodProviders).getName();
- }
-
- // Remove altitude requirement
- criteria.setAltitudeRequired(false);
- goodProviders = getProviders(criteria, enabledOnly);
- if (!goodProviders.isEmpty()) {
- return best(goodProviders).getName();
- }
-
return null;
}
@@ -658,7 +437,7 @@
if (listener == null) {
throw new IllegalArgumentException("listener==null");
}
- _requestLocationUpdates(provider, minTime, minDistance, listener, null);
+ _requestLocationUpdates(provider, null, minTime, minDistance, false, listener, null);
}
/**
@@ -701,10 +480,10 @@
* each location update
* @param looper a Looper object whose message queue will be used to
* implement the callback mechanism.
+ * If looper is null then the callbacks will be called on the main thread.
*
* @throws IllegalArgumentException if provider is null or doesn't exist
* @throws IllegalArgumentException if listener is null
- * @throws IllegalArgumentException if looper is null
* @throws SecurityException if no suitable permission is present for the provider.
*/
public void requestLocationUpdates(String provider,
@@ -716,15 +495,70 @@
if (listener == null) {
throw new IllegalArgumentException("listener==null");
}
- if (looper == null) {
- throw new IllegalArgumentException("looper==null");
- }
- _requestLocationUpdates(provider, minTime, minDistance, listener, looper);
+ _requestLocationUpdates(provider, null, minTime, minDistance, false, listener, looper);
}
- private void _requestLocationUpdates(String provider,
- long minTime, float minDistance, LocationListener listener,
- Looper looper) {
+ /**
+ * Registers the current activity to be notified periodically based on
+ * the specified criteria. Periodically, the supplied LocationListener will
+ * be called with the current Location or with status updates.
+ *
+ * <p> It may take a while to receive the most recent location. If
+ * an immediate location is required, applications may use the
+ * {@link #getLastKnownLocation(String)} method.
+ *
+ * <p> In case the provider is disabled by the user, updates will stop,
+ * and the {@link LocationListener#onProviderDisabled(String)}
+ * method will be called. As soon as the provider is enabled again,
+ * the {@link LocationListener#onProviderEnabled(String)} method will
+ * be called and location updates will start again.
+ *
+ * <p> The frequency of notification may be controlled using the
+ * minTime and minDistance parameters. If minTime is greater than 0,
+ * the LocationManager could potentially rest for minTime milliseconds
+ * between location updates to conserve power. If minDistance is greater than 0,
+ * a location will only be broadcasted if the device moves by minDistance meters.
+ * To obtain notifications as frequently as possible, set both parameters to 0.
+ *
+ * <p> Background services should be careful about setting a sufficiently high
+ * minTime so that the device doesn't consume too much power by keeping the
+ * GPS or wireless radios on all the time. In particular, values under 60000ms
+ * are not recommended.
+ *
+ * <p> The supplied Looper is used to implement the callback mechanism.
+ *
+ * @param minTime the minimum time interval for notifications, in
+ * milliseconds. This field is only used as a hint to conserve power, and actual
+ * time between location updates may be greater or lesser than this value.
+ * @param minDistance the minimum distance interval for notifications,
+ * in meters
+ * @param criteria contains parameters for the location manager to choose the
+ * appropriate provider and parameters to compute the location
+ * @param listener a {#link LocationListener} whose
+ * {@link LocationListener#onLocationChanged} method will be called for
+ * each location update
+ * @param looper a Looper object whose message queue will be used to
+ * implement the callback mechanism.
+ * If looper is null then the callbacks will be called on the main thread.
+ *
+ * @throws IllegalArgumentException if criteria is null
+ * @throws IllegalArgumentException if listener is null
+ * @throws SecurityException if no suitable permission is present to access
+ * the location services.
+ */
+ public void requestLocationUpdates(long minTime, float minDistance,
+ Criteria criteria, LocationListener listener, Looper looper) {
+ if (criteria == null) {
+ throw new IllegalArgumentException("criteria==null");
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("listener==null");
+ }
+ _requestLocationUpdates(null, criteria, minTime, minDistance, false, listener, looper);
+ }
+
+ private void _requestLocationUpdates(String provider, Criteria criteria, long minTime,
+ float minDistance, boolean singleShot, LocationListener listener, Looper looper) {
if (minTime < 0L) {
minTime = 0L;
}
@@ -739,7 +573,7 @@
transport = new ListenerTransport(listener, looper);
}
mListeners.put(listener, transport);
- mService.requestLocationUpdates(provider, minTime, minDistance, transport);
+ mService.requestLocationUpdates(provider, criteria, minTime, minDistance, singleShot, transport);
}
} catch (RemoteException ex) {
Log.e(TAG, "requestLocationUpdates: DeadObjectException", ex);
@@ -785,7 +619,7 @@
* time between location updates may be greater or lesser than this value.
* @param minDistance the minimum distance interval for notifications,
* in meters
- * @param intent a {#link PendingIntet} to be sent for each location update
+ * @param intent a {#link PendingIntent} to be sent for each location update
*
* @throws IllegalArgumentException if provider is null or doesn't exist
* @throws IllegalArgumentException if intent is null
@@ -799,11 +633,67 @@
if (intent == null) {
throw new IllegalArgumentException("intent==null");
}
- _requestLocationUpdates(provider, minTime, minDistance, intent);
+ _requestLocationUpdates(provider, null, minTime, minDistance, false, intent);
}
- private void _requestLocationUpdates(String provider,
- long minTime, float minDistance, PendingIntent intent) {
+ /**
+ * Registers the current activity to be notified periodically based on
+ * the specified criteria. Periodically, the supplied PendingIntent will
+ * be broadcast with the current Location or with status updates.
+ *
+ * <p> Location updates are sent with a key of KEY_LOCATION_CHANGED and a Location value.
+ *
+ * <p> It may take a while to receive the most recent location. If
+ * an immediate location is required, applications may use the
+ * {@link #getLastKnownLocation(String)} method.
+ *
+ * <p> The frequency of notification or new locations may be controlled using the
+ * minTime and minDistance parameters. If minTime is greater than 0,
+ * the LocationManager could potentially rest for minTime milliseconds
+ * between location updates to conserve power. If minDistance is greater than 0,
+ * a location will only be broadcast if the device moves by minDistance meters.
+ * To obtain notifications as frequently as possible, set both parameters to 0.
+ *
+ * <p> Background services should be careful about setting a sufficiently high
+ * minTime so that the device doesn't consume too much power by keeping the
+ * GPS or wireless radios on all the time. In particular, values under 60000ms
+ * are not recommended.
+ *
+ * <p> In case the provider is disabled by the user, updates will stop,
+ * and an intent will be sent with an extra with key KEY_PROVIDER_ENABLED and a boolean value
+ * of false. If the provider is re-enabled, an intent will be sent with an
+ * extra with key KEY_PROVIDER_ENABLED and a boolean value of true and location updates will
+ * start again.
+ *
+ * <p> If the provider's status changes, an intent will be sent with an extra with key
+ * KEY_STATUS_CHANGED and an integer value indicating the new status. Any extras associated
+ * with the status update will be sent as well.
+ *
+ * @param minTime the minimum time interval for notifications, in
+ * milliseconds. This field is only used as a hint to conserve power, and actual
+ * time between location updates may be greater or lesser than this value.
+ * @param minDistance the minimum distance interval for notifications,
+ * in meters
+ * @param criteria contains parameters for the location manager to choose the
+ * appropriate provider and parameters to compute the location
+ * @param intent a {#link PendingIntent} to be sent for each location update
+ *
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * @throws IllegalArgumentException if intent is null
+ * @throws SecurityException if no suitable permission is present for the provider.
+ */
+ public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria, PendingIntent intent) {
+ if (criteria == null) {
+ throw new IllegalArgumentException("criteria==null");
+ }
+ if (intent == null) {
+ throw new IllegalArgumentException("intent==null");
+ }
+ _requestLocationUpdates(null, criteria, minTime, minDistance, false, intent);
+ }
+
+ private void _requestLocationUpdates(String provider, Criteria criteria,
+ long minTime, float minDistance, boolean singleShot, PendingIntent intent) {
if (minTime < 0L) {
minTime = 0L;
}
@@ -812,13 +702,150 @@
}
try {
- mService.requestLocationUpdatesPI(provider, minTime, minDistance, intent);
+ mService.requestLocationUpdatesPI(provider, criteria, minTime, minDistance, singleShot, intent);
} catch (RemoteException ex) {
Log.e(TAG, "requestLocationUpdates: RemoteException", ex);
}
}
/**
+ * Registers the current activity to be notified periodically by
+ * the named provider. Periodically, the supplied LocationListener will
+ * be called with the current Location or with status updates.
+ *
+ * <p> It may take a while to receive the most recent location. If
+ * an immediate location is required, applications may use the
+ * {@link #getLastKnownLocation(String)} method.
+ *
+ * <p> In case the provider is disabled by the user, updates will stop,
+ * and the {@link LocationListener#onProviderDisabled(String)}
+ * method will be called. As soon as the provider is enabled again,
+ * the {@link LocationListener#onProviderEnabled(String)} method will
+ * be called and location updates will start again.
+ *
+ * <p> The supplied Looper is used to implement the callback mechanism.
+ *
+ * @param provider the name of the provider with which to register
+ * @param listener a {#link LocationListener} whose
+ * {@link LocationListener#onLocationChanged} method will be called for
+ * each location update
+ * @param looper a Looper object whose message queue will be used to
+ * implement the callback mechanism.
+ * If looper is null then the callbacks will be called on the main thread.
+ *
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * @throws IllegalArgumentException if listener is null
+ * @throws SecurityException if no suitable permission is present for the provider.
+ */
+ public void requestSingleUpdate(String provider, LocationListener listener, Looper looper) {
+ if (provider == null) {
+ throw new IllegalArgumentException("provider==null");
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("listener==null");
+ }
+ _requestLocationUpdates(provider, null, 0L, 0.0f, true, listener, looper);
+ }
+
+ /**
+ * Registers the current activity to be notified periodically based on
+ * the specified criteria. Periodically, the supplied LocationListener will
+ * be called with the current Location or with status updates.
+ *
+ * <p> It may take a while to receive the most recent location. If
+ * an immediate location is required, applications may use the
+ * {@link #getLastKnownLocation(String)} method.
+ *
+ * <p> In case the provider is disabled by the user, updates will stop,
+ * and the {@link LocationListener#onProviderDisabled(String)}
+ * method will be called. As soon as the provider is enabled again,
+ * the {@link LocationListener#onProviderEnabled(String)} method will
+ * be called and location updates will start again.
+ *
+ * <p> The supplied Looper is used to implement the callback mechanism.
+ *
+ * @param criteria contains parameters for the location manager to choose the
+ * appropriate provider and parameters to compute the location
+ * @param listener a {#link LocationListener} whose
+ * {@link LocationListener#onLocationChanged} method will be called for
+ * each location update
+ * @param looper a Looper object whose message queue will be used to
+ * implement the callback mechanism.
+ * If looper is null then the callbacks will be called on the current thread.
+ *
+ * @throws IllegalArgumentException if criteria is null
+ * @throws IllegalArgumentException if listener is null
+ * @throws SecurityException if no suitable permission is present to access
+ * the location services.
+ */
+ public void requestSingleUpdate(Criteria criteria, LocationListener listener, Looper looper) {
+ if (criteria == null) {
+ throw new IllegalArgumentException("criteria==null");
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("listener==null");
+ }
+ _requestLocationUpdates(null, criteria, 0L, 0.0f, true, listener, looper);
+ }
+
+ /**
+ * Registers the current activity to be notified periodically by
+ * the named provider. Periodically, the supplied PendingIntent will
+ * be broadcast with the current Location or with status updates.
+ *
+ * <p> Location updates are sent with a key of KEY_LOCATION_CHANGED and a Location value.
+ *
+ * <p> It may take a while to receive the most recent location. If
+ * an immediate location is required, applications may use the
+ * {@link #getLastKnownLocation(String)} method.
+ *
+ * @param provider the name of the provider with which to register
+ * @param intent a {#link PendingIntent} to be sent for the location update
+ *
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * @throws IllegalArgumentException if intent is null
+ * @throws SecurityException if no suitable permission is present for the provider.
+ */
+ public void requestSingleUpdate(String provider, PendingIntent intent) {
+ if (provider == null) {
+ throw new IllegalArgumentException("provider==null");
+ }
+ if (intent == null) {
+ throw new IllegalArgumentException("intent==null");
+ }
+ _requestLocationUpdates(provider, null, 0L, 0.0f, true, intent);
+ }
+
+ /**
+ * Registers the current activity to be notified periodically based on
+ * the specified criteria. Periodically, the supplied PendingIntent will
+ * be broadcast with the current Location or with status updates.
+ *
+ * <p> Location updates are sent with a key of KEY_LOCATION_CHANGED and a Location value.
+ *
+ * <p> It may take a while to receive the most recent location. If
+ * an immediate location is required, applications may use the
+ * {@link #getLastKnownLocation(String)} method.
+ *
+ * @param criteria contains parameters for the location manager to choose the
+ * appropriate provider and parameters to compute the location
+ * @param intent a {#link PendingIntent} to be sent for the location update
+ *
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * @throws IllegalArgumentException if intent is null
+ * @throws SecurityException if no suitable permission is present for the provider.
+ */
+ public void requestSingleUpdate(Criteria criteria, PendingIntent intent) {
+ if (criteria == null) {
+ throw new IllegalArgumentException("criteria==null");
+ }
+ if (intent == null) {
+ throw new IllegalArgumentException("intent==null");
+ }
+ _requestLocationUpdates(null, criteria, 0L, 0.0f, true, intent);
+ }
+
+ /**
* Removes any current registration for location updates of the current activity
* with the given LocationListener. Following this call, updates will no longer
* occur for this listener.
diff --git a/location/java/android/location/LocationProvider.java b/location/java/android/location/LocationProvider.java
index bb3e2a5..8c16580 100644
--- a/location/java/android/location/LocationProvider.java
+++ b/location/java/android/location/LocationProvider.java
@@ -16,6 +16,9 @@
package android.location;
+import android.os.RemoteException;
+import android.util.Log;
+
/**
* An abstract superclass for location providers. A location provider
* provides periodic reports on the geographical location of the
@@ -36,7 +39,8 @@
// in the name of a LocationProvider.
static final String BAD_CHARS_REGEX = "[^a-zA-Z0-9]";
- private String mName;
+ private final String mName;
+ private final ILocationManager mService;
public static final int OUT_OF_SERVICE = 0;
public static final int TEMPORARILY_UNAVAILABLE = 1;
@@ -50,13 +54,13 @@
*
* {@hide}
*/
- public LocationProvider(String name) {
+ public LocationProvider(String name, ILocationManager service) {
if (name.matches(BAD_CHARS_REGEX)) {
throw new IllegalArgumentException("name " + name +
" contains an illegal character");
}
- // Log.d(TAG, "Constructor: name = " + name);
mName = name;
+ mService = service;
}
/**
@@ -71,29 +75,12 @@
* false otherwise.
*/
public boolean meetsCriteria(Criteria criteria) {
- // We do not want to match the special passive provider based on criteria.
- if (LocationManager.PASSIVE_PROVIDER.equals(mName)) {
+ try {
+ return mService.providerMeetsCriteria(mName, criteria);
+ } catch (RemoteException e) {
+ Log.e(TAG, "meetsCriteria: RemoteException", e);
return false;
}
- if ((criteria.getAccuracy() != Criteria.NO_REQUIREMENT) &&
- (criteria.getAccuracy() < getAccuracy())) {
- return false;
- }
- int criteriaPower = criteria.getPowerRequirement();
- if ((criteriaPower != Criteria.NO_REQUIREMENT) &&
- (criteriaPower < getPowerRequirement())) {
- return false;
- }
- if (criteria.isAltitudeRequired() && !supportsAltitude()) {
- return false;
- }
- if (criteria.isSpeedRequired() && !supportsSpeed()) {
- return false;
- }
- if (criteria.isBearingRequired() && !supportsBearing()) {
- return false;
- }
- return true;
}
/**
diff --git a/location/java/android/location/provider/GeocodeProvider.java b/location/java/android/location/provider/GeocodeProvider.java
index 86376a7..9a58763 100644
--- a/location/java/android/location/provider/GeocodeProvider.java
+++ b/location/java/android/location/provider/GeocodeProvider.java
@@ -29,8 +29,6 @@
* outside of the core android platform.
* Geocode providers can be implemented as services and return the result of
* {@link GeocodeProvider#getBinder()} in its getBinder() method.
- *
- * @hide
*/
public abstract class GeocodeProvider {
@@ -53,7 +51,7 @@
/**
* This method is overridden to implement the
- * {@link Geocoder#getFromLocation(double, double, int)} method.
+ * {@link android.location.Geocoder#getFromLocation(double, double, int)} method.
* Classes implementing this method should not hold a reference to the params parameter.
*/
public abstract String onGetFromLocation(double latitude, double longitude, int maxResults,
@@ -61,7 +59,7 @@
/**
* This method is overridden to implement the
- * {@link Geocoder#getFromLocationName(String, int, double, double, double, double)} method.
+ * {@link android.location.Geocoder#getFromLocationName(String, int, double, double, double, double)} method.
* Classes implementing this method should not hold a reference to the params parameter.
*/
public abstract String onGetFromLocationName(String locationName,
diff --git a/location/java/android/location/provider/LocationProvider.java b/location/java/android/location/provider/LocationProvider.java
index 56cfb33..cf939de 100644
--- a/location/java/android/location/provider/LocationProvider.java
+++ b/location/java/android/location/provider/LocationProvider.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.net.NetworkInfo;
+import android.location.Criteria;
import android.location.ILocationManager;
import android.location.ILocationProvider;
import android.location.Location;
@@ -32,8 +33,6 @@
* outside of the core android platform.
* Location providers can be implemented as services and return the result of
* {@link LocationProvider#getBinder()} in its getBinder() method.
- *
- * @hide
*/
public abstract class LocationProvider {
@@ -75,6 +74,10 @@
return LocationProvider.this.onGetPowerRequirement();
}
+ public boolean meetsCriteria(Criteria criteria) {
+ return LocationProvider.this.onMeetsCriteria(criteria);
+ }
+
public int getAccuracy() {
return LocationProvider.this.onGetAccuracy();
}
@@ -226,6 +229,12 @@
public abstract int onGetPowerRequirement();
/**
+ * Returns true if this provider meets the given criteria,
+ * false otherwise.
+ */
+ public abstract boolean onMeetsCriteria(Criteria criteria);
+
+ /**
* Returns a constant describing horizontal accuracy of this provider.
* If the provider returns finer grain or exact location,
* {@link Criteria#ACCURACY_FINE} is returned, otherwise if the
@@ -246,11 +255,11 @@
/**
* Returns a information on the status of this provider.
- * {@link #OUT_OF_SERVICE} is returned if the provider is
+ * {@link android.location.LocationProvider#OUT_OF_SERVICE} is returned if the provider is
* out of service, and this is not expected to change in the near
- * future; {@link #TEMPORARILY_UNAVAILABLE} is returned if
+ * future; {@link android.location.LocationProvider#TEMPORARILY_UNAVAILABLE} is returned if
* the provider is temporarily unavailable but is expected to be
- * available shortly; and {@link #AVAILABLE} is returned
+ * available shortly; and {@link android.location.LocationProvider#AVAILABLE} is returned
* if the provider is currently available.
*
* <p> If extras is non-null, additional status information may be
@@ -299,9 +308,9 @@
/**
* Updates the network state for the given provider. This function must
- * be overwritten if {@link #requiresNetwork} returns true. The state is
- * {@link #TEMPORARILY_UNAVAILABLE} (disconnected), OR {@link #AVAILABLE}
- * (connected or connecting).
+ * be overwritten if {@link android.location.LocationProvider#requiresNetwork} returns true.
+ * The state is {@link android.location.LocationProvider#TEMPORARILY_UNAVAILABLE} (disconnected)
+ * OR {@link android.location.LocationProvider#AVAILABLE} (connected or connecting).
*
* @param state data state
*/
diff --git a/location/java/com/android/internal/location/DummyLocationProvider.java b/location/java/com/android/internal/location/DummyLocationProvider.java
index ff5e27b..e7b5143 100644
--- a/location/java/com/android/internal/location/DummyLocationProvider.java
+++ b/location/java/com/android/internal/location/DummyLocationProvider.java
@@ -16,6 +16,7 @@
package com.android.internal.location;
+import android.location.ILocationManager;
import android.location.LocationProvider;
/**
@@ -41,8 +42,8 @@
int mPowerRequirement;
int mAccuracy;
- public DummyLocationProvider(String name) {
- super(name);
+ public DummyLocationProvider(String name, ILocationManager service) {
+ super(name, service);
}
public void setRequiresNetwork(boolean requiresNetwork) {
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index 9d1d420..f1da72f 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -46,8 +46,9 @@
public static final int FILE_TYPE_WMA = 6;
public static final int FILE_TYPE_OGG = 7;
public static final int FILE_TYPE_AAC = 8;
+ public static final int FILE_TYPE_MKA = 9;
private static final int FIRST_AUDIO_FILE_TYPE = FILE_TYPE_MP3;
- private static final int LAST_AUDIO_FILE_TYPE = FILE_TYPE_AAC;
+ private static final int LAST_AUDIO_FILE_TYPE = FILE_TYPE_MKA;
// MIDI file types
public static final int FILE_TYPE_MID = 11;
@@ -63,8 +64,9 @@
public static final int FILE_TYPE_3GPP2 = 24;
public static final int FILE_TYPE_WMV = 25;
public static final int FILE_TYPE_ASF = 26;
+ public static final int FILE_TYPE_MKV = 27;
private static final int FIRST_VIDEO_FILE_TYPE = FILE_TYPE_MP4;
- private static final int LAST_VIDEO_FILE_TYPE = FILE_TYPE_ASF;
+ private static final int LAST_VIDEO_FILE_TYPE = FILE_TYPE_MKV;
// Image file types
public static final int FILE_TYPE_JPEG = 31;
@@ -134,6 +136,7 @@
addFileType("OGG", FILE_TYPE_OGG, "application/ogg");
addFileType("OGA", FILE_TYPE_OGG, "application/ogg");
addFileType("AAC", FILE_TYPE_AAC, "audio/aac");
+ addFileType("MKA", FILE_TYPE_MKA, "audio/x-matroska");
addFileType("MID", FILE_TYPE_MID, "audio/midi");
addFileType("MIDI", FILE_TYPE_MID, "audio/midi");
@@ -151,6 +154,8 @@
addFileType("3GPP", FILE_TYPE_3GPP, "video/3gpp");
addFileType("3G2", FILE_TYPE_3GPP2, "video/3gpp2");
addFileType("3GPP2", FILE_TYPE_3GPP2, "video/3gpp2");
+ addFileType("MKV", FILE_TYPE_MKV, "video/x-matroska");
+ addFileType("WEBM", FILE_TYPE_MKV, "video/x-matroska");
if (isWMVEnabled()) {
addFileType("WMV", FILE_TYPE_WMV, "video/x-ms-wmv");
addFileType("ASF", FILE_TYPE_ASF, "video/x-ms-asf");
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 8404779..de64714 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -584,8 +584,8 @@
// Check on video frame rate
int frameRate = newCameraParams.getPreviewFrameRate();
if (frameRate < 0 || (frameRate - mFrameRate) != 0) {
- LOGE("Failed to set frame rate to %d", mFrameRate);
- return UNKNOWN_ERROR;
+ LOGE("Failed to set frame rate to %d fps. The actual "
+ "frame rate is %d", mFrameRate, frameRate);
}
CHECK_EQ(OK, mCamera->setPreviewDisplay(mPreviewSurface));
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index e54fb67..681943f 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -75,7 +75,8 @@
libstagefright_avcdec \
libstagefright_m4vh263dec \
libstagefright_mp3dec \
- libstagefright_vorbisdec
+ libstagefright_vorbisdec \
+ libstagefright_matroska \
LOCAL_SHARED_LIBRARIES += \
libstagefright_amrnb_common \
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index f668caa..274dad9 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -822,7 +822,12 @@
CHECK(mVideoTrack->getFormat()->findInt32(kKeyWidth, &mVideoWidth));
CHECK(mVideoTrack->getFormat()->findInt32(kKeyHeight, &mVideoHeight));
- mVideoSource->start();
+ status_t err = mVideoSource->start();
+
+ if (err != OK) {
+ mVideoSource.clear();
+ return err;
+ }
}
return mVideoSource != NULL ? OK : UNKNOWN_ERROR;
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index a66f86b..475422d 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -20,6 +20,8 @@
#include "include/WAVExtractor.h"
#include "include/OggExtractor.h"
+#include "matroska/MatroskaExtractor.h"
+
#include <media/stagefright/CachingDataSource.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/FileSource.h>
@@ -94,6 +96,7 @@
RegisterSniffer(SniffAMR);
RegisterSniffer(SniffWAV);
RegisterSniffer(SniffOgg);
+ RegisterSniffer(SniffMatroska);
}
// static
diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp
index 4b3813b..327a0ce 100644
--- a/media/libstagefright/MediaDefs.cpp
+++ b/media/libstagefright/MediaDefs.cpp
@@ -20,6 +20,7 @@
const char *MEDIA_MIMETYPE_IMAGE_JPEG = "image/jpeg";
+const char *MEDIA_MIMETYPE_VIDEO_VPX = "video/x-vnd.on2.vp8";
const char *MEDIA_MIMETYPE_VIDEO_AVC = "video/avc";
const char *MEDIA_MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es";
const char *MEDIA_MIMETYPE_VIDEO_H263 = "video/3gpp";
@@ -36,5 +37,6 @@
const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mpeg4";
const char *MEDIA_MIMETYPE_CONTAINER_WAV = "audio/wav";
const char *MEDIA_MIMETYPE_CONTAINER_OGG = "application/ogg";
+const char *MEDIA_MIMETYPE_CONTAINER_MATROSKA = "video/x-matroska";
} // namespace android
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index 513f49c..376d715 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -24,6 +24,8 @@
#include "include/WAVExtractor.h"
#include "include/OggExtractor.h"
+#include "matroska/MatroskaExtractor.h"
+
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaExtractor.h>
@@ -69,6 +71,8 @@
return new WAVExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_OGG)) {
return new OggExtractor(source);
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MATROSKA)) {
+ return new MatroskaExtractor(source);
}
return NULL;
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 33a118b..928a6c8 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -932,8 +932,6 @@
h264type.bEnableFMO = OMX_FALSE;
h264type.bEnableASO = OMX_FALSE;
h264type.bEnableRS = OMX_FALSE;
- h264type.eProfile = OMX_VIDEO_AVCProfileBaseline;
- h264type.eLevel = OMX_VIDEO_AVCLevel1b;
h264type.bFrameMBsOnly = OMX_TRUE;
h264type.bMBAFF = OMX_FALSE;
h264type.bEntropyCodingCABAC = OMX_FALSE;
diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index 03287dd1..ab17b04 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -42,7 +42,8 @@
static const char *kValidExtensions[] = {
".mp3", ".mp4", ".m4a", ".3gp", ".3gpp", ".3g2", ".3gpp2",
".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac",
- ".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota"
+ ".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota",
+ ".mkv", ".mka", ".webm"
};
static const size_t kNumValidExtensions =
sizeof(kValidExtensions) / sizeof(kValidExtensions[0]);
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index 2968917..9d89c20 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -120,7 +120,11 @@
return NULL;
}
- decoder->start();
+ status_t err = decoder->start();
+ if (err != OK) {
+ LOGW("OMXCodec::start returned error %d (0x%08x)\n", err, err);
+ return NULL;
+ }
// Read one output buffer, ignore format change notifications
// and spurious empty buffers.
@@ -134,7 +138,6 @@
}
MediaBuffer *buffer = NULL;
- status_t err;
do {
if (buffer != NULL) {
buffer->release();
@@ -375,6 +378,24 @@
// The duration value is a string representing the duration in ms.
sprintf(tmp, "%lld", (maxDurationUs + 500) / 1000);
mMetaData.add(METADATA_KEY_DURATION, String8(tmp));
+
+ if (numTracks == 1) {
+ const char *fileMIME;
+ CHECK(meta->findCString(kKeyMIMEType, &fileMIME));
+
+ if (!strcasecmp(fileMIME, "video/x-matroska")) {
+ sp<MetaData> trackMeta = mExtractor->getTrackMetaData(0);
+ const char *trackMIME;
+ CHECK(trackMeta->findCString(kKeyMIMEType, &trackMIME));
+
+ if (!strncasecmp("audio/", trackMIME, 6)) {
+ // The matroska file only contains a single audio track,
+ // rewrite its mime type.
+ mMetaData.add(
+ METADATA_KEY_MIMETYPE, String8("audio/x-matroska"));
+ }
+ }
+ }
}
diff --git a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
index aa2a3d1..24c361e 100644
--- a/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
+++ b/media/libstagefright/codecs/avc/dec/AVCDecoder.cpp
@@ -34,6 +34,8 @@
namespace android {
+static const char kStartCode[4] = { 0x00, 0x00, 0x00, 0x01 };
+
static int32_t Malloc(void *userData, int32_t size, int32_t attrs) {
return reinterpret_cast<int32_t>(malloc(size));
}
@@ -154,9 +156,7 @@
}
}
- sp<MetaData> params = new MetaData;
- params->setInt32(kKeyWantsNALFragments, true);
- mSource->start(params.get());
+ mSource->start();
mAnchorTimeUs = 0;
mNumSamplesOutput = 0;
@@ -167,9 +167,10 @@
}
void AVCDecoder::addCodecSpecificData(const uint8_t *data, size_t size) {
- MediaBuffer *buffer = new MediaBuffer(size);
- memcpy(buffer->data(), data, size);
- buffer->set_range(0, size);
+ MediaBuffer *buffer = new MediaBuffer(size + 4);
+ memcpy(buffer->data(), kStartCode, 4);
+ memcpy((uint8_t *)buffer->data() + 4, data, size);
+ buffer->set_range(0, size + 4);
mCodecSpecificData.push(buffer);
}
@@ -200,6 +201,29 @@
return mFormat;
}
+static void findNALFragment(
+ const MediaBuffer *buffer, const uint8_t **fragPtr, size_t *fragSize) {
+ const uint8_t *data =
+ (const uint8_t *)buffer->data() + buffer->range_offset();
+
+ size_t size = buffer->range_length();
+
+ CHECK(size >= 4);
+ CHECK(!memcmp(kStartCode, data, 4));
+
+ size_t offset = 4;
+ while (offset + 3 < size && memcmp(kStartCode, &data[offset], 4)) {
+ ++offset;
+ }
+
+ *fragPtr = &data[4];
+ if (offset + 3 >= size) {
+ *fragSize = size - 4;
+ } else {
+ *fragSize = offset - 4;
+ }
+}
+
status_t AVCDecoder::read(
MediaBuffer **out, const ReadOptions *options) {
*out = NULL;
@@ -254,37 +278,31 @@
}
}
- const uint8_t *inPtr =
- (const uint8_t *)mInputBuffer->data() + mInputBuffer->range_offset();
+ const uint8_t *fragPtr;
+ size_t fragSize;
+ findNALFragment(mInputBuffer, &fragPtr, &fragSize);
+
+ bool releaseFragment = true;
+ status_t err = UNKNOWN_ERROR;
int nalType;
int nalRefIdc;
AVCDec_Status res =
PVAVCDecGetNALType(
- const_cast<uint8_t *>(inPtr), mInputBuffer->range_length(),
+ const_cast<uint8_t *>(fragPtr), fragSize,
&nalType, &nalRefIdc);
if (res != AVCDEC_SUCCESS) {
LOGE("cannot determine nal type");
-
- mInputBuffer->release();
- mInputBuffer = NULL;
-
- return UNKNOWN_ERROR;
- }
-
- switch (nalType) {
+ } else switch (nalType) {
case AVC_NALTYPE_SPS:
{
res = PVAVCDecSeqParamSet(
- mHandle, const_cast<uint8_t *>(inPtr),
- mInputBuffer->range_length());
+ mHandle, const_cast<uint8_t *>(fragPtr),
+ fragSize);
if (res != AVCDEC_SUCCESS) {
- mInputBuffer->release();
- mInputBuffer = NULL;
-
- return UNKNOWN_ERROR;
+ break;
}
AVCDecObject *pDecVid = (AVCDecObject *)mHandle->AVCObject;
@@ -324,47 +342,53 @@
int32_t aligned_width = (crop_right - crop_left + 1 + 15) & ~15;
int32_t aligned_height = (crop_bottom - crop_top + 1 + 15) & ~15;
- mFormat->setInt32(kKeyWidth, aligned_width);
- mFormat->setInt32(kKeyHeight, aligned_height);
- mInputBuffer->release();
- mInputBuffer = NULL;
+ int32_t oldWidth, oldHeight;
+ CHECK(mFormat->findInt32(kKeyWidth, &oldWidth));
+ CHECK(mFormat->findInt32(kKeyHeight, &oldHeight));
- return INFO_FORMAT_CHANGED;
+ if (oldWidth != aligned_width || oldHeight != aligned_height) {
+ mFormat->setInt32(kKeyWidth, aligned_width);
+ mFormat->setInt32(kKeyHeight, aligned_height);
+
+ err = INFO_FORMAT_CHANGED;
+ } else {
+ *out = new MediaBuffer(0);
+ err = OK;
+ }
+ break;
}
case AVC_NALTYPE_PPS:
{
res = PVAVCDecPicParamSet(
- mHandle, const_cast<uint8_t *>(inPtr),
- mInputBuffer->range_length());
-
- mInputBuffer->release();
- mInputBuffer = NULL;
+ mHandle, const_cast<uint8_t *>(fragPtr),
+ fragSize);
if (res != AVCDEC_SUCCESS) {
- return UNKNOWN_ERROR;
+ break;
}
*out = new MediaBuffer(0);
- return OK;
+ err = OK;
+ break;
}
case AVC_NALTYPE_SLICE:
case AVC_NALTYPE_IDR:
{
res = PVAVCDecodeSlice(
- mHandle, const_cast<uint8_t *>(inPtr),
- mInputBuffer->range_length());
+ mHandle, const_cast<uint8_t *>(fragPtr),
+ fragSize);
if (res == AVCDEC_PICTURE_OUTPUT_READY) {
int32_t index;
int32_t Release;
AVCFrameIO Output;
Output.YCbCr[0] = Output.YCbCr[1] = Output.YCbCr[2] = NULL;
- CHECK_EQ(PVAVCDecGetOutput(
- mHandle, &index, &Release, &Output),
+
+ CHECK_EQ(PVAVCDecGetOutput(mHandle, &index, &Release, &Output),
AVCDEC_SUCCESS);
CHECK(index >= 0);
@@ -376,48 +400,44 @@
// Do _not_ release input buffer yet.
- return OK;
+ releaseFragment = false;
+ err = OK;
+ break;
}
- mInputBuffer->release();
- mInputBuffer = NULL;
-
if (res == AVCDEC_PICTURE_READY || res == AVCDEC_SUCCESS) {
*out = new MediaBuffer(0);
- return OK;
+ err = OK;
} else {
LOGV("failed to decode frame (res = %d)", res);
- return UNKNOWN_ERROR;
}
+ break;
}
case AVC_NALTYPE_SEI:
{
res = PVAVCDecSEI(
- mHandle, const_cast<uint8_t *>(inPtr),
- mInputBuffer->range_length());
-
- mInputBuffer->release();
- mInputBuffer = NULL;
+ mHandle, const_cast<uint8_t *>(fragPtr),
+ fragSize);
if (res != AVCDEC_SUCCESS) {
- return UNKNOWN_ERROR;
+ break;
}
*out = new MediaBuffer(0);
- return OK;
+ err = OK;
+ break;
}
case AVC_NALTYPE_AUD:
+ case AVC_NALTYPE_FILL:
{
- mInputBuffer->release();
- mInputBuffer = NULL;
-
*out = new MediaBuffer(0);
- return OK;
+ err = OK;
+ break;
}
default:
@@ -428,10 +448,19 @@
}
}
- mInputBuffer->release();
- mInputBuffer = NULL;
+ if (releaseFragment) {
+ size_t offset = mInputBuffer->range_offset();
+ if (fragSize + 4 == mInputBuffer->range_length()) {
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ } else {
+ mInputBuffer->set_range(
+ offset + fragSize + 4,
+ mInputBuffer->range_length() - fragSize - 4);
+ }
+ }
- return UNKNOWN_ERROR;
+ return err;
}
// static
diff --git a/media/libstagefright/codecs/m4v_h263/dec/M4vH263Decoder.cpp b/media/libstagefright/codecs/m4v_h263/dec/M4vH263Decoder.cpp
index 40009f8..8350f7a 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/M4vH263Decoder.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/M4vH263Decoder.cpp
@@ -120,9 +120,16 @@
vol_size = 0;
}
- CHECK_EQ(PV_TRUE, PVInitVideoDecoder(
- mHandle, vol_data, &vol_size, 1, mWidth, mHeight, mode));
+
+ Bool success = PVInitVideoDecoder(
+ mHandle, vol_data, &vol_size, 1, mWidth, mHeight, mode);
if (vol_data[0]) free(vol_data[0]);
+
+ if (success != PV_TRUE) {
+ LOGW("PVInitVideoDecoder failed. Unsupported content?");
+ return ERROR_UNSUPPORTED;
+ }
+
MP4DecodingMode actualMode = PVGetDecBitstreamMode(mHandle);
CHECK_EQ(mode, actualMode);
diff --git a/media/libstagefright/matroska/Android.mk b/media/libstagefright/matroska/Android.mk
new file mode 100644
index 0000000..0e72198
--- /dev/null
+++ b/media/libstagefright/matroska/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ MatroskaExtractor.cpp \
+ mkvparser.cpp \
+
+LOCAL_C_INCLUDES:= \
+ $(JNI_H_INCLUDE) \
+ $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
+
+LOCAL_CFLAGS += -Wno-multichar
+
+LOCAL_MODULE:= libstagefright_matroska
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp
new file mode 100644
index 0000000..197ccf8
--- /dev/null
+++ b/media/libstagefright/matroska/MatroskaExtractor.cpp
@@ -0,0 +1,504 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MatroskaExtractor"
+#include <utils/Log.h>
+
+#include "MatroskaExtractor.h"
+
+#include "mkvparser.hpp"
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <utils/String8.h>
+
+namespace android {
+
+struct DataSourceReader : public mkvparser::IMkvReader {
+ DataSourceReader(const sp<DataSource> &source)
+ : mSource(source) {
+ }
+
+ virtual int Read(long long position, long length, unsigned char* buffer) {
+ CHECK(position >= 0);
+ CHECK(length >= 0);
+
+ if (length == 0) {
+ return 0;
+ }
+
+ ssize_t n = mSource->readAt(position, buffer, length);
+
+ if (n <= 0) {
+ return -1;
+ }
+
+ return 0;
+ }
+
+ virtual int Length(long long* total, long long* available) {
+ off_t size;
+ if (mSource->getSize(&size) != OK) {
+ return -1;
+ }
+
+ if (total) {
+ *total = size;
+ }
+
+ if (available) {
+ *available = size;
+ }
+
+ return 0;
+ }
+
+private:
+ sp<DataSource> mSource;
+
+ DataSourceReader(const DataSourceReader &);
+ DataSourceReader &operator=(const DataSourceReader &);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+#include <ctype.h>
+static void hexdump(const void *_data, size_t size) {
+ const uint8_t *data = (const uint8_t *)_data;
+ size_t offset = 0;
+ while (offset < size) {
+ printf("0x%04x ", offset);
+
+ size_t n = size - offset;
+ if (n > 16) {
+ n = 16;
+ }
+
+ for (size_t i = 0; i < 16; ++i) {
+ if (i == 8) {
+ printf(" ");
+ }
+
+ if (offset + i < size) {
+ printf("%02x ", data[offset + i]);
+ } else {
+ printf(" ");
+ }
+ }
+
+ printf(" ");
+
+ for (size_t i = 0; i < n; ++i) {
+ if (isprint(data[offset + i])) {
+ printf("%c", data[offset + i]);
+ } else {
+ printf(".");
+ }
+ }
+
+ printf("\n");
+
+ offset += 16;
+ }
+}
+
+struct MatroskaSource : public MediaSource {
+ MatroskaSource(
+ const sp<MatroskaExtractor> &extractor, size_t index);
+
+ virtual status_t start(MetaData *params);
+ virtual status_t stop();
+
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options);
+
+private:
+ enum Type {
+ AVC,
+ AAC,
+ OTHER
+ };
+
+ sp<MatroskaExtractor> mExtractor;
+ size_t mTrackIndex;
+ unsigned long mTrackNum;
+ Type mType;
+ mkvparser::Cluster *mCluster;
+ const mkvparser::BlockEntry *mBlockEntry;
+
+ status_t advance();
+
+ MatroskaSource(const MatroskaSource &);
+ MatroskaSource &operator=(const MatroskaSource &);
+};
+
+MatroskaSource::MatroskaSource(
+ const sp<MatroskaExtractor> &extractor, size_t index)
+ : mExtractor(extractor),
+ mTrackIndex(index),
+ mType(OTHER),
+ mCluster(NULL),
+ mBlockEntry(NULL) {
+ mTrackNum = mExtractor->mTracks.itemAt(index).mTrackNum;
+
+ const char *mime;
+ CHECK(mExtractor->mTracks.itemAt(index).mMeta->
+ findCString(kKeyMIMEType, &mime));
+
+ if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
+ mType = AVC;
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
+ mType = AAC;
+ }
+}
+
+status_t MatroskaSource::start(MetaData *params) {
+ mCluster = NULL;
+ mBlockEntry = NULL;
+
+ return OK;
+}
+
+status_t MatroskaSource::stop() {
+ return OK;
+}
+
+sp<MetaData> MatroskaSource::getFormat() {
+ return mExtractor->mTracks.itemAt(mTrackIndex).mMeta;
+}
+
+status_t MatroskaSource::advance() {
+ for (;;) {
+ if (mBlockEntry == NULL || mBlockEntry->EOS()) {
+ if (mCluster == NULL) {
+ mCluster = mExtractor->mSegment->GetFirst();
+ } else {
+ mCluster = mExtractor->mSegment->GetNext(mCluster);
+ }
+ if (mCluster == NULL || mCluster->EOS()) {
+ return ERROR_END_OF_STREAM;
+ }
+ mBlockEntry = mCluster->GetFirst();
+ }
+
+ if (mBlockEntry->GetBlock()->GetTrackNumber() != mTrackNum) {
+ mBlockEntry = mCluster->GetNext(mBlockEntry);
+ continue;
+ }
+
+ break;
+ }
+
+ return OK;
+}
+
+status_t MatroskaSource::read(
+ MediaBuffer **out, const ReadOptions *options) {
+ *out = NULL;
+
+ int64_t seekTimeUs;
+ if (options && options->getSeekTo(&seekTimeUs)) {
+ mBlockEntry = NULL;
+ mCluster = mExtractor->mSegment->GetCluster(seekTimeUs * 1000ll);
+
+ status_t err;
+ while ((err = advance()) == OK && !mBlockEntry->GetBlock()->IsKey()) {
+ mBlockEntry = mCluster->GetNext(mBlockEntry);
+ }
+
+ if (err != OK) {
+ return ERROR_END_OF_STREAM;
+ }
+ }
+
+ if (advance() != OK) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ const mkvparser::Block *block = mBlockEntry->GetBlock();
+ size_t size = block->GetSize();
+ long long timeNs = block->GetTime(mCluster);
+
+ MediaBuffer *buffer = new MediaBuffer(size + 2);
+ buffer->meta_data()->setInt64(kKeyTime, (timeNs + 500) / 1000);
+
+ long res = block->Read(
+ mExtractor->mReader, (unsigned char *)buffer->data() + 2);
+
+ if (res != 0) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ buffer->set_range(2, size);
+
+ if (mType == AVC) {
+ CHECK(size >= 2);
+
+ uint8_t *data = (uint8_t *)buffer->data();
+
+ unsigned NALsize = data[2] << 8 | data[3];
+ CHECK_EQ(size, NALsize + 2);
+
+ memcpy(data, "\x00\x00\x00\x01", 4);
+ buffer->set_range(0, size + 2);
+ } else if (mType == AAC) {
+ // There's strange junk at the beginning...
+
+ const uint8_t *data = (const uint8_t *)buffer->data() + 2;
+ size_t offset = 0;
+ while (offset < size && data[offset] != 0x21) {
+ ++offset;
+ }
+ buffer->set_range(2 + offset, size - offset);
+ }
+
+ *out = buffer;
+
+#if 0
+ hexdump((const uint8_t *)buffer->data() + buffer->range_offset(),
+ buffer->range_length());
+#endif
+
+ mBlockEntry = mCluster->GetNext(mBlockEntry);
+
+ return OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+MatroskaExtractor::MatroskaExtractor(const sp<DataSource> &source)
+ : mDataSource(source),
+ mReader(new DataSourceReader(mDataSource)),
+ mSegment(NULL) {
+ mkvparser::EBMLHeader ebmlHeader;
+ long long pos;
+ if (ebmlHeader.Parse(mReader, pos) < 0) {
+ return;
+ }
+
+ long long ret =
+ mkvparser::Segment::CreateInstance(mReader, pos, mSegment);
+
+ if (ret) {
+ CHECK(mSegment == NULL);
+ return;
+ }
+
+ ret = mSegment->Load();
+
+ if (ret < 0) {
+ delete mSegment;
+ mSegment = NULL;
+ return;
+ }
+
+ addTracks();
+}
+
+MatroskaExtractor::~MatroskaExtractor() {
+ delete mSegment;
+ mSegment = NULL;
+
+ delete mReader;
+ mReader = NULL;
+}
+
+size_t MatroskaExtractor::countTracks() {
+ return mTracks.size();
+}
+
+sp<MediaSource> MatroskaExtractor::getTrack(size_t index) {
+ if (index >= mTracks.size()) {
+ return NULL;
+ }
+
+ return new MatroskaSource(this, index);
+}
+
+sp<MetaData> MatroskaExtractor::getTrackMetaData(
+ size_t index, uint32_t flags) {
+ if (index >= mTracks.size()) {
+ return NULL;
+ }
+
+ return mTracks.itemAt(index).mMeta;
+}
+
+static void addESDSFromAudioSpecificInfo(
+ const sp<MetaData> &meta, const void *asi, size_t asiSize) {
+ static const uint8_t kStaticESDS[] = {
+ 0x03, 22,
+ 0x00, 0x00, // ES_ID
+ 0x00, // streamDependenceFlag, URL_Flag, OCRstreamFlag
+
+ 0x04, 17,
+ 0x40, // Audio ISO/IEC 14496-3
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+
+ 0x05,
+ // AudioSpecificInfo (with size prefix) follows
+ };
+
+ CHECK(asiSize < 128);
+ size_t esdsSize = sizeof(kStaticESDS) + asiSize + 1;
+ uint8_t *esds = new uint8_t[esdsSize];
+ memcpy(esds, kStaticESDS, sizeof(kStaticESDS));
+ uint8_t *ptr = esds + sizeof(kStaticESDS);
+ *ptr++ = asiSize;
+ memcpy(ptr, asi, asiSize);
+
+ meta->setData(kKeyESDS, 0, esds, esdsSize);
+
+ delete[] esds;
+ esds = NULL;
+}
+
+void addVorbisCodecInfo(
+ const sp<MetaData> &meta,
+ const void *_codecPrivate, size_t codecPrivateSize) {
+ // printf("vorbis private data follows:\n");
+ // hexdump(_codecPrivate, codecPrivateSize);
+
+ CHECK(codecPrivateSize >= 3);
+
+ const uint8_t *codecPrivate = (const uint8_t *)_codecPrivate;
+ CHECK(codecPrivate[0] == 0x02);
+
+ size_t len1 = codecPrivate[1];
+ size_t len2 = codecPrivate[2];
+
+ CHECK(codecPrivateSize > 3 + len1 + len2);
+
+ CHECK(codecPrivate[3] == 0x01);
+ meta->setData(kKeyVorbisInfo, 0, &codecPrivate[3], len1);
+
+ CHECK(codecPrivate[len1 + 3] == 0x03);
+
+ CHECK(codecPrivate[len1 + len2 + 3] == 0x05);
+ meta->setData(
+ kKeyVorbisBooks, 0, &codecPrivate[len1 + len2 + 3],
+ codecPrivateSize - len1 - len2 - 3);
+}
+
+void MatroskaExtractor::addTracks() {
+ const mkvparser::Tracks *tracks = mSegment->GetTracks();
+
+ for (size_t index = 0; index < tracks->GetTracksCount(); ++index) {
+ const mkvparser::Track *track = tracks->GetTrackByIndex(index);
+
+ const char *const codecID = track->GetCodecId();
+ LOGV("codec id = %s", codecID);
+ LOGV("codec name = %s", track->GetCodecNameAsUTF8());
+
+ size_t codecPrivateSize;
+ const unsigned char *codecPrivate =
+ track->GetCodecPrivate(&codecPrivateSize);
+
+ enum { VIDEO_TRACK = 1, AUDIO_TRACK = 2 };
+
+ sp<MetaData> meta = new MetaData;
+
+ switch (track->GetType()) {
+ case VIDEO_TRACK:
+ {
+ const mkvparser::VideoTrack *vtrack =
+ static_cast<const mkvparser::VideoTrack *>(track);
+
+ if (!strcmp("V_MPEG4/ISO/AVC", codecID)) {
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+ meta->setData(kKeyAVCC, 0, codecPrivate, codecPrivateSize);
+ } else if (!strcmp("V_VP8", codecID)) {
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_VPX);
+ } else {
+ continue;
+ }
+
+ meta->setInt32(kKeyWidth, vtrack->GetWidth());
+ meta->setInt32(kKeyHeight, vtrack->GetHeight());
+ break;
+ }
+
+ case AUDIO_TRACK:
+ {
+ const mkvparser::AudioTrack *atrack =
+ static_cast<const mkvparser::AudioTrack *>(track);
+
+ if (!strcmp("A_AAC", codecID)) {
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
+ CHECK(codecPrivateSize >= 2);
+
+ addESDSFromAudioSpecificInfo(
+ meta, codecPrivate, codecPrivateSize);
+ } else if (!strcmp("A_VORBIS", codecID)) {
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_VORBIS);
+
+ addVorbisCodecInfo(meta, codecPrivate, codecPrivateSize);
+ } else {
+ continue;
+ }
+
+ meta->setInt32(kKeySampleRate, atrack->GetSamplingRate());
+ meta->setInt32(kKeyChannelCount, atrack->GetChannels());
+ break;
+ }
+
+ default:
+ continue;
+ }
+
+ long long durationNs = mSegment->GetDuration();
+ meta->setInt64(kKeyDuration, (durationNs + 500) / 1000);
+
+ mTracks.push();
+ TrackInfo *trackInfo = &mTracks.editItemAt(mTracks.size() - 1);
+ trackInfo->mTrackNum = track->GetNumber();
+ trackInfo->mMeta = meta;
+ }
+}
+
+sp<MetaData> MatroskaExtractor::getMetaData() {
+ sp<MetaData> meta = new MetaData;
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MATROSKA);
+
+ return meta;
+}
+
+bool SniffMatroska(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+ DataSourceReader reader(source);
+ mkvparser::EBMLHeader ebmlHeader;
+ long long pos;
+ if (ebmlHeader.Parse(&reader, pos) < 0) {
+ return false;
+ }
+
+ mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_MATROSKA);
+ *confidence = 0.6;
+
+ return true;
+}
+
+} // namespace android
diff --git a/media/libstagefright/matroska/MatroskaExtractor.h b/media/libstagefright/matroska/MatroskaExtractor.h
new file mode 100644
index 0000000..7bf41a9
--- /dev/null
+++ b/media/libstagefright/matroska/MatroskaExtractor.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MATROSKA_EXTRACTOR_H_
+
+#define MATROSKA_EXTRACTOR_H_
+
+#include <media/stagefright/MediaExtractor.h>
+#include <utils/Vector.h>
+
+namespace mkvparser {
+struct Segment;
+};
+
+namespace android {
+
+class String8;
+
+struct DataSourceReader;
+struct MatroskaSource;
+
+struct MatroskaExtractor : public MediaExtractor {
+ MatroskaExtractor(const sp<DataSource> &source);
+
+ virtual size_t countTracks();
+
+ virtual sp<MediaSource> getTrack(size_t index);
+
+ virtual sp<MetaData> getTrackMetaData(
+ size_t index, uint32_t flags);
+
+ virtual sp<MetaData> getMetaData();
+
+protected:
+ virtual ~MatroskaExtractor();
+
+private:
+ friend struct MatroskaSource;
+
+ struct TrackInfo {
+ unsigned long mTrackNum;
+ sp<MetaData> mMeta;
+ };
+ Vector<TrackInfo> mTracks;
+
+ sp<DataSource> mDataSource;
+ DataSourceReader *mReader;
+ mkvparser::Segment *mSegment;
+
+ void addTracks();
+
+ MatroskaExtractor(const MatroskaExtractor &);
+ MatroskaExtractor &operator=(const MatroskaExtractor &);
+};
+
+bool SniffMatroska(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence);
+
+} // namespace android
+
+#endif // MATROSKA_EXTRACTOR_H_
diff --git a/media/libstagefright/matroska/mkvparser.cpp b/media/libstagefright/matroska/mkvparser.cpp
new file mode 100644
index 0000000..4e51004
--- /dev/null
+++ b/media/libstagefright/matroska/mkvparser.cpp
@@ -0,0 +1,3103 @@
+#include "mkvparser.hpp"
+#include <cassert>
+#include <cstring>
+
+mkvparser::IMkvReader::~IMkvReader()
+{
+}
+
+long long mkvparser::ReadUInt(IMkvReader* pReader, long long pos, long& len)
+{
+ assert(pReader);
+ assert(pos >= 0);
+
+ long long total, available;
+
+ long hr = pReader->Length(&total, &available);
+ assert(hr >= 0);
+ assert(pos < available);
+ assert((available - pos) >= 1); //assume here max u-int len is 8
+
+ unsigned char b;
+
+ hr = pReader->Read(pos, 1, &b);
+ if (hr < 0)
+ return hr;
+
+ assert(hr == 0L);
+
+ if (b & 0x80) //1000 0000
+ {
+ len = 1;
+ b &= 0x7F; //0111 1111
+ }
+ else if (b & 0x40) //0100 0000
+ {
+ len = 2;
+ b &= 0x3F; //0011 1111
+ }
+ else if (b & 0x20) //0010 0000
+ {
+ len = 3;
+ b &= 0x1F; //0001 1111
+ }
+ else if (b & 0x10) //0001 0000
+ {
+ len = 4;
+ b &= 0x0F; //0000 1111
+ }
+ else if (b & 0x08) //0000 1000
+ {
+ len = 5;
+ b &= 0x07; //0000 0111
+ }
+ else if (b & 0x04) //0000 0100
+ {
+ len = 6;
+ b &= 0x03; //0000 0011
+ }
+ else if (b & 0x02) //0000 0010
+ {
+ len = 7;
+ b &= 0x01; //0000 0001
+ }
+ else
+ {
+ assert(b & 0x01); //0000 0001
+ len = 8;
+ b = 0; //0000 0000
+ }
+
+ assert((available - pos) >= len);
+
+ long long result = b;
+ ++pos;
+ for (long i = 1; i < len; ++i)
+ {
+ hr = pReader->Read(pos, 1, &b);
+
+ if (hr < 0)
+ return hr;
+
+ assert(hr == 0L);
+
+ result <<= 8;
+ result |= b;
+
+ ++pos;
+ }
+
+ return result;
+}
+
+
+long long mkvparser::GetUIntLength(
+ IMkvReader* pReader,
+ long long pos,
+ long& len)
+{
+ assert(pReader);
+ assert(pos >= 0);
+
+ long long total, available;
+
+ long hr = pReader->Length(&total, &available);
+ assert(hr >= 0);
+ assert(available <= total);
+
+ if (pos >= available)
+ return pos; //too few bytes available
+
+ unsigned char b;
+
+ hr = pReader->Read(pos, 1, &b);
+
+ if (hr < 0)
+ return hr;
+
+ assert(hr == 0L);
+
+ if (b == 0) //we can't handle u-int values larger than 8 bytes
+ return E_FILE_FORMAT_INVALID;
+
+ unsigned char m = 0x80;
+ len = 1;
+
+ while (!(b & m))
+ {
+ m >>= 1;
+ ++len;
+ }
+
+ return 0; //success
+}
+
+
+long long mkvparser::SyncReadUInt(
+ IMkvReader* pReader,
+ long long pos,
+ long long stop,
+ long& len)
+{
+ assert(pReader);
+
+ if (pos >= stop)
+ return E_FILE_FORMAT_INVALID;
+
+ unsigned char b;
+
+ long hr = pReader->Read(pos, 1, &b);
+
+ if (hr < 0)
+ return hr;
+
+ if (hr != 0L)
+ return E_BUFFER_NOT_FULL;
+
+ if (b == 0) //we can't handle u-int values larger than 8 bytes
+ return E_FILE_FORMAT_INVALID;
+
+ unsigned char m = 0x80;
+ len = 1;
+
+ while (!(b & m))
+ {
+ m >>= 1;
+ ++len;
+ }
+
+ if ((pos + len) > stop)
+ return E_FILE_FORMAT_INVALID;
+
+ long long result = b & (~m);
+ ++pos;
+
+ for (int i = 1; i < len; ++i)
+ {
+ hr = pReader->Read(pos, 1, &b);
+
+ if (hr < 0)
+ return hr;
+
+ if (hr != 0L)
+ return E_BUFFER_NOT_FULL;
+
+ result <<= 8;
+ result |= b;
+
+ ++pos;
+ }
+
+ return result;
+}
+
+
+long long mkvparser::UnserializeUInt(
+ IMkvReader* pReader,
+ long long pos,
+ long long size)
+{
+ assert(pReader);
+ assert(pos >= 0);
+ assert(size > 0);
+ assert(size <= 8);
+
+ long long result = 0;
+
+ for (long long i = 0; i < size; ++i)
+ {
+ unsigned char b;
+
+ const long hr = pReader->Read(pos, 1, &b);
+
+ if (hr < 0)
+ return hr;
+ result <<= 8;
+ result |= b;
+
+ ++pos;
+ }
+
+ return result;
+}
+
+
+float mkvparser::Unserialize4Float(
+ IMkvReader* pReader,
+ long long pos)
+{
+ assert(pReader);
+ assert(pos >= 0);
+
+ long long total, available;
+
+ long hr = pReader->Length(&total, &available);
+ assert(hr >= 0);
+ assert(available <= total);
+ assert((pos + 4) <= available);
+
+ float result;
+
+ unsigned char* const p = (unsigned char*)&result;
+ unsigned char* q = p + 4;
+
+ for (;;)
+ {
+ hr = pReader->Read(pos, 1, --q);
+ assert(hr == 0L);
+
+ if (q == p)
+ break;
+
+ ++pos;
+ }
+
+ return result;
+}
+
+
+double mkvparser::Unserialize8Double(
+ IMkvReader* pReader,
+ long long pos)
+{
+ assert(pReader);
+ assert(pos >= 0);
+
+ double result;
+
+ unsigned char* const p = (unsigned char*)&result;
+ unsigned char* q = p + 8;
+
+ for (;;)
+ {
+ const long hr = pReader->Read(pos, 1, --q);
+ assert(hr == 0L);
+
+ if (q == p)
+ break;
+
+ ++pos;
+ }
+
+ return result;
+}
+
+signed char mkvparser::Unserialize1SInt(
+ IMkvReader* pReader,
+ long long pos)
+{
+ assert(pReader);
+ assert(pos >= 0);
+
+ long long total, available;
+
+ long hr = pReader->Length(&total, &available);
+ assert(hr == 0);
+ assert(available <= total);
+ assert(pos < available);
+
+ signed char result;
+
+ hr = pReader->Read(pos, 1, (unsigned char*)&result);
+ assert(hr == 0);
+
+ return result;
+}
+
+short mkvparser::Unserialize2SInt(
+ IMkvReader* pReader,
+ long long pos)
+{
+ assert(pReader);
+ assert(pos >= 0);
+
+ long long total, available;
+
+ long hr = pReader->Length(&total, &available);
+ assert(hr >= 0);
+ assert(available <= total);
+ assert((pos + 2) <= available);
+
+ short result;
+
+ unsigned char* const p = (unsigned char*)&result;
+ unsigned char* q = p + 2;
+
+ for (;;)
+ {
+ hr = pReader->Read(pos, 1, --q);
+ assert(hr == 0L);
+
+ if (q == p)
+ break;
+
+ ++pos;
+ }
+
+ return result;
+}
+
+
+bool mkvparser::Match(
+ IMkvReader* pReader,
+ long long& pos,
+ unsigned long id_,
+ long long& val)
+
+{
+ assert(pReader);
+ assert(pos >= 0);
+
+ long long total, available;
+
+ long hr = pReader->Length(&total, &available);
+ assert(hr >= 0);
+ assert(available <= total);
+
+ long len;
+
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0);
+ assert(len > 0);
+ assert(len <= 8);
+ assert((pos + len) <= available);
+
+ if ((unsigned long)id != id_)
+ return false;
+
+ pos += len; //consume id
+
+ const long long size = ReadUInt(pReader, pos, len);
+ assert(size >= 0);
+ assert(size <= 8);
+ assert(len > 0);
+ assert(len <= 8);
+ assert((pos + len) <= available);
+
+ pos += len; //consume length of size of payload
+
+ val = UnserializeUInt(pReader, pos, size);
+ assert(val >= 0);
+
+ pos += size; //consume size of payload
+
+ return true;
+}
+
+bool mkvparser::Match(
+ IMkvReader* pReader,
+ long long& pos,
+ unsigned long id_,
+ char*& val)
+{
+ assert(pReader);
+ assert(pos >= 0);
+
+ long long total, available;
+
+ long hr = pReader->Length(&total, &available);
+ assert(hr >= 0);
+ assert(available <= total);
+
+ long len;
+
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0);
+ assert(len > 0);
+ assert(len <= 8);
+ assert((pos + len) <= available);
+
+ if ((unsigned long)id != id_)
+ return false;
+
+ pos += len; //consume id
+
+ const long long size_ = ReadUInt(pReader, pos, len);
+ assert(size_ >= 0);
+ assert(len > 0);
+ assert(len <= 8);
+ assert((pos + len) <= available);
+
+ pos += len; //consume length of size of payload
+ assert((pos + size_) <= available);
+
+ const size_t size = static_cast<size_t>(size_);
+ val = new char[size+1];
+
+ for (size_t i = 0; i < size; ++i)
+ {
+ char c;
+
+ hr = pReader->Read(pos + i, 1, (unsigned char*)&c);
+ assert(hr == 0L);
+
+ val[i] = c;
+
+ if (c == '\0')
+ break;
+
+ }
+
+ val[size] = '\0';
+ pos += size_; //consume size of payload
+
+ return true;
+}
+
+#if 0
+bool mkvparser::Match(
+ IMkvReader* pReader,
+ long long& pos,
+ unsigned long id,
+ wchar_t*& val)
+{
+ char* str;
+
+ if (!Match(pReader, pos, id, str))
+ return false;
+
+ const size_t size = mbstowcs(NULL, str, 0);
+
+ if (size == 0)
+ val = NULL;
+ else
+ {
+ val = new wchar_t[size+1];
+ mbstowcs(val, str, size);
+ val[size] = L'\0';
+ }
+
+ delete[] str;
+ return true;
+}
+#endif
+
+
+bool mkvparser::Match(
+ IMkvReader* pReader,
+ long long& pos,
+ unsigned long id_,
+ unsigned char*& val,
+ size_t *optionalSize)
+{
+ assert(pReader);
+ assert(pos >= 0);
+
+ long long total, available;
+
+ long hr = pReader->Length(&total, &available);
+ assert(hr >= 0);
+ assert(available <= total);
+
+ long len;
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0);
+ assert(len > 0);
+ assert(len <= 8);
+ assert((pos + len) <= available);
+
+ if ((unsigned long)id != id_)
+ return false;
+
+ pos += len; //consume id
+
+ const long long size_ = ReadUInt(pReader, pos, len);
+ assert(size_ >= 0);
+ assert(len > 0);
+ assert(len <= 8);
+ assert((pos + len) <= available);
+
+ pos += len; //consume length of size of payload
+ assert((pos + size_) <= available);
+
+ const size_t size = static_cast<size_t>(size_);
+ val = new unsigned char[size];
+
+ if (optionalSize) {
+ *optionalSize = size;
+ }
+
+ for (size_t i = 0; i < size; ++i)
+ {
+ unsigned char b;
+
+ hr = pReader->Read(pos + i, 1, &b);
+ assert(hr == 0L);
+
+ val[i] = b;
+ }
+
+ pos += size_; //consume size of payload
+ return true;
+}
+
+
+bool mkvparser::Match(
+ IMkvReader* pReader,
+ long long& pos,
+ unsigned long id_,
+ double& val)
+{
+ assert(pReader);
+ assert(pos >= 0);
+
+ long long total, available;
+
+ long hr = pReader->Length(&total, &available);
+ assert(hr >= 0);
+ assert(available <= total);
+ long idlen;
+ const long long id = ReadUInt(pReader, pos, idlen);
+ assert(id >= 0); //TODO
+
+ if ((unsigned long)id != id_)
+ return false;
+
+ long sizelen;
+ const long long size = ReadUInt(pReader, pos + idlen, sizelen);
+
+ switch (size)
+ {
+ case 4:
+ case 8:
+ break;
+ default:
+ return false;
+ }
+
+ pos += idlen + sizelen; //consume id and size fields
+ assert((pos + size) <= available);
+
+ if (size == 4)
+ val = Unserialize4Float(pReader, pos);
+ else
+ {
+ assert(size == 8);
+ val = Unserialize8Double(pReader, pos);
+ }
+
+ pos += size; //consume size of payload
+
+ return true;
+}
+
+
+bool mkvparser::Match(
+ IMkvReader* pReader,
+ long long& pos,
+ unsigned long id_,
+ short& val)
+{
+ assert(pReader);
+ assert(pos >= 0);
+
+ long long total, available;
+
+ long hr = pReader->Length(&total, &available);
+ assert(hr >= 0);
+ assert(available <= total);
+
+ long len;
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0);
+ assert((pos + len) <= available);
+
+ if ((unsigned long)id != id_)
+ return false;
+
+ pos += len; //consume id
+
+ const long long size = ReadUInt(pReader, pos, len);
+ assert(size <= 2);
+ assert((pos + len) <= available);
+
+ pos += len; //consume length of size of payload
+ assert((pos + size) <= available);
+
+ //TODO:
+ // Generalize this to work for any size signed int
+ if (size == 1)
+ val = Unserialize1SInt(pReader, pos);
+ else
+ val = Unserialize2SInt(pReader, pos);
+
+ pos += size; //consume size of payload
+
+ return true;
+}
+
+
+namespace mkvparser
+{
+
+EBMLHeader::EBMLHeader():
+ m_docType(NULL)
+{
+}
+
+EBMLHeader::~EBMLHeader()
+{
+ delete[] m_docType;
+}
+
+long long EBMLHeader::Parse(
+ IMkvReader* pReader,
+ long long& pos)
+{
+ assert(pReader);
+
+ long long total, available;
+
+ long hr = pReader->Length(&total, &available);
+
+ if (hr < 0)
+ return hr;
+
+ pos = 0;
+ long long end = (1024 < available)? 1024: available;
+
+ for (;;)
+ {
+ unsigned char b = 0;
+
+ while (pos < end)
+ {
+ hr = pReader->Read(pos, 1, &b);
+
+ if (hr < 0)
+ return hr;
+
+ if (b == 0x1A)
+ break;
+
+ ++pos;
+ }
+
+ if (b != 0x1A)
+ {
+ if ((pos >= 1024) ||
+ (available >= total) ||
+ ((total - available) < 5))
+ return -1;
+
+ return available + 5; //5 = 4-byte ID + 1st byte of size
+ }
+
+ if ((total - pos) < 5)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((available - pos) < 5)
+ return pos + 5; //try again later
+
+ long len;
+
+ const long long result = ReadUInt(pReader, pos, len);
+
+ if (result < 0) //error
+ return result;
+
+ if (result == 0x0A45DFA3) //ReadId masks-off length indicator bits
+ {
+ assert(len == 4);
+ pos += len;
+ break;
+ }
+
+ ++pos; //throw away just the 0x1A byte, and try again
+ }
+
+ long len;
+ long long result = GetUIntLength(pReader, pos, len);
+
+ if (result < 0) //error
+ return result;
+
+ if (result > 0) //need more data
+ return result;
+
+ assert(len > 0);
+ assert(len <= 8);
+
+ if ((total - pos) < len)
+ return E_FILE_FORMAT_INVALID;
+ if ((available - pos) < len)
+ return pos + len; //try again later
+
+ result = ReadUInt(pReader, pos, len);
+
+ if (result < 0) //error
+ return result;
+
+ pos += len; //consume u-int
+
+ if ((total - pos) < result)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((available - pos) < result)
+ return pos + result;
+
+ end = pos + result;
+
+ m_version = 1;
+ m_readVersion = 1;
+ m_maxIdLength = 4;
+ m_maxSizeLength = 8;
+ m_docTypeVersion = 1;
+ m_docTypeReadVersion = 1;
+
+ while (pos < end)
+ {
+ if (Match(pReader, pos, 0x0286, m_version))
+ ;
+ else if (Match(pReader, pos, 0x02F7, m_readVersion))
+ ;
+ else if (Match(pReader, pos, 0x02F2, m_maxIdLength))
+ ;
+ else if (Match(pReader, pos, 0x02F3, m_maxSizeLength))
+ ;
+ else if (Match(pReader, pos, 0x0282, m_docType))
+ ;
+ else if (Match(pReader, pos, 0x0287, m_docTypeVersion))
+ ;
+ else if (Match(pReader, pos, 0x0285, m_docTypeReadVersion))
+ ;
+ else
+ {
+ result = ReadUInt(pReader, pos, len);
+ assert(result > 0);
+ assert(len > 0);
+ assert(len <= 8);
+
+ pos += len;
+ assert(pos < end);
+
+ result = ReadUInt(pReader, pos, len);
+ assert(result >= 0);
+ assert(len > 0);
+ assert(len <= 8);
+
+ pos += len + result;
+ assert(pos <= end);
+ }
+ }
+
+ assert(pos == end);
+
+ return 0;
+}
+
+
+Segment::Segment(
+ IMkvReader* pReader,
+ long long start,
+ long long size) :
+ m_pReader(pReader),
+ m_start(start),
+ m_size(size),
+ m_pos(start),
+ m_pInfo(NULL),
+ m_pTracks(NULL),
+ m_clusterCount(0)
+ //m_clusterNumber(0)
+{
+}
+
+
+Segment::~Segment()
+{
+ Cluster** i = m_clusters;
+ Cluster** j = m_clusters + m_clusterCount;
+
+ while (i != j)
+ {
+ Cluster* p = *i++;
+ assert(p);
+ delete p;
+ }
+
+ delete[] m_clusters;
+
+ delete m_pTracks;
+ delete m_pInfo;
+}
+
+
+long long Segment::CreateInstance(
+ IMkvReader* pReader,
+ long long pos,
+ Segment*& pSegment)
+{
+ assert(pReader);
+ assert(pos >= 0);
+
+ pSegment = NULL;
+
+ long long total, available;
+
+ long hr = pReader->Length(&total, &available);
+ assert(hr >= 0);
+ assert(available <= total);
+
+ //I would assume that in practice this loop would execute
+ //exactly once, but we allow for other elements (e.g. Void)
+ //to immediately follow the EBML header. This is fine for
+ //the source filter case (since the entire file is available),
+ //but in the splitter case over a network we should probably
+ //just give up early. We could for example decide only to
+ //execute this loop a maximum of, say, 10 times.
+
+ while (pos < total)
+ {
+ //Read ID
+
+ long len;
+ long long result = GetUIntLength(pReader, pos, len);
+
+ if (result) //error, or too few available bytes
+ return result;
+
+ if ((pos + len) > total)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > available)
+ return pos + len;
+
+ //TODO: if we liberalize the behavior of ReadUInt, we can
+ //probably eliminate having to use GetUIntLength here.
+ const long long id = ReadUInt(pReader, pos, len);
+
+ if (id < 0) //error
+ return id;
+
+ pos += len; //consume ID
+
+ //Read Size
+
+ result = GetUIntLength(pReader, pos, len);
+
+ if (result) //error, or too few available bytes
+ return result;
+
+ if ((pos + len) > total)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > available)
+ return pos + len;
+
+ //TODO: if we liberalize the behavior of ReadUInt, we can
+ //probably eliminate having to use GetUIntLength here.
+ const long long size = ReadUInt(pReader, pos, len);
+
+ if (size < 0)
+ return size;
+
+ pos += len; //consume length of size of element
+
+ //Pos now points to start of payload
+
+ if ((pos + size) > total)
+ return E_FILE_FORMAT_INVALID;
+
+ if (id == 0x08538067) //Segment ID
+ {
+ pSegment = new Segment(pReader, pos, size);
+ assert(pSegment); //TODO
+
+ return 0; //success
+ }
+
+ pos += size; //consume payload
+ }
+
+ assert(pos == total);
+
+ pSegment = new Segment(pReader, pos, 0);
+ assert(pSegment); //TODO
+
+ return 0; //success (sort of)
+}
+
+
+long long Segment::ParseHeaders()
+{
+ //Outermost (level 0) segment object has been constructed,
+ //and pos designates start of payload. We need to find the
+ //inner (level 1) elements.
+ long long total, available;
+
+ long hr = m_pReader->Length(&total, &available);
+ assert(hr >= 0);
+ assert(available <= total);
+
+ const long long stop = m_start + m_size;
+ assert(stop <= total);
+ assert(m_pos <= stop);
+
+ bool bQuit = false;
+ while ((m_pos < stop) && !bQuit)
+ {
+ long long pos = m_pos;
+
+ long len;
+ long long result = GetUIntLength(m_pReader, pos, len);
+
+ if (result) //error, or too few available bytes
+ return result;
+
+ if ((pos + len) > stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > available)
+ return pos + len;
+
+ const long long idpos = pos;
+ const long long id = ReadUInt(m_pReader, idpos, len);
+
+ if (id < 0) //error
+ return id;
+
+ pos += len; //consume ID
+
+ //Read Size
+ result = GetUIntLength(m_pReader, pos, len);
+
+ if (result) //error, or too few available bytes
+ return result;
+
+ if ((pos + len) > stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if ((pos + len) > available)
+ return pos + len;
+
+ const long long size = ReadUInt(m_pReader, pos, len);
+
+ if (size < 0)
+ return size;
+
+ pos += len; //consume length of size of element
+
+ //Pos now points to start of payload
+
+ if ((pos + size) > stop)
+ return E_FILE_FORMAT_INVALID;
+
+ //We read EBML elements either in total or nothing at all.
+
+ if ((pos + size) > available)
+ return pos + size;
+
+ if (id == 0x0549A966) //Segment Info ID
+ {
+ assert(m_pInfo == NULL);
+ m_pInfo = new SegmentInfo(this, pos, size);
+ assert(m_pInfo); //TODO
+
+ if (m_pTracks)
+ bQuit = true;
+ }
+ else if (id == 0x0654AE6B) //Tracks ID
+ {
+ assert(m_pTracks == NULL);
+ m_pTracks = new Tracks(this, pos, size);
+ assert(m_pTracks); //TODO
+
+ if (m_pInfo)
+ bQuit = true;
+ }
+ else if (id == 0x0F43B675) //Cluster ID
+ {
+#if 0
+ if (m_pInfo == NULL) //TODO: liberalize
+ ;
+ else if (m_pTracks == NULL)
+ ;
+ else
+ //ParseCluster(idpos, pos, size);
+ Cluster::Parse(this, m_clusters, pos, size);
+#endif
+ bQuit = true;
+ }
+
+ m_pos = pos + size; //consume payload
+ }
+
+ assert(m_pos <= stop);
+
+ return 0; //success
+}
+
+
+long Segment::ParseCluster(Cluster*& pCluster, long long& pos_) const
+{
+ pCluster = NULL;
+ pos_ = -1;
+
+ const long long stop = m_start + m_size;
+ assert(m_pos <= stop);
+
+ long long pos = m_pos;
+ long long off = -1;
+
+
+ while (pos < stop)
+ {
+ long len;
+ const long long idpos = pos;
+
+ const long long id = SyncReadUInt(m_pReader, pos, stop, len);
+
+ if (id < 0) //error
+ return static_cast<long>(id);
+
+ if (id == 0)
+ return E_FILE_FORMAT_INVALID;
+
+ pos += len; //consume id
+ assert(pos < stop);
+
+ const long long size = SyncReadUInt(m_pReader, pos, stop, len);
+
+ if (size < 0) //error
+ return static_cast<long>(size);
+
+ pos += len; //consume size
+ assert(pos <= stop);
+
+ if (size == 0) //weird
+ continue;
+
+ //pos now points to start of payload
+
+ pos += size; //consume payload
+ assert(pos <= stop);
+
+ if (off >= 0)
+ {
+ pos_ = idpos;
+ break;
+ }
+
+ if (id == 0x0F43B675) //Cluster ID
+ off = idpos - m_start;
+ }
+
+ Segment* const this_ = const_cast<Segment*>(this);
+ const size_t idx = m_clusterCount;
+
+ if (pos >= stop)
+ {
+ pos_ = stop;
+
+#if 0
+ if (off < 0)
+ {
+ pCluster = Cluster::CreateEndOfStream(this_, idx);
+ return 1L;
+ }
+#else
+ if (off < 0)
+ return 1L;
+#endif
+
+ //Reading 0 bytes at pos might work too -- it would depend
+ //on how the reader is implemented.
+
+ unsigned char b;
+
+ const long hr = m_pReader->Read(pos - 1, 1, &b);
+
+ if (hr < 0)
+ return hr;
+
+ if (hr != 0L)
+ return E_BUFFER_NOT_FULL;
+ }
+
+ assert(off >= 0);
+ assert(pos_ >= m_start);
+ assert(pos_ <= stop);
+
+ pCluster = Cluster::Parse(this_, idx, off);
+ return 0L;
+}
+
+
+bool Segment::AddCluster(Cluster* pCluster, long long pos)
+{
+ assert(pos >= m_start);
+
+ const long long stop = m_start + m_size;
+ assert(pos <= stop);
+
+ if (pCluster)
+ m_clusters[pos] = pCluster;
+
+ m_pos = pos; //m_pos >= stop is now we know we have all clusters
+
+ return (pos >= stop);
+}
+
+
+long Segment::Load()
+{
+ //Outermost (level 0) segment object has been constructed,
+ //and pos designates start of payload. We need to find the
+ //inner (level 1) elements.
+ const long long stop = m_start + m_size;
+#ifdef _DEBUG
+ {
+ long long total, available;
+
+ long hr = m_pReader->Length(&total, &available);
+ assert(hr >= 0);
+ assert(available >= total);
+ assert(stop <= total);
+ }
+#endif
+ long long index = m_pos;
+
+ m_clusterCount = 0;
+
+ while (index < stop)
+ {
+ long len = 0;
+
+ long long result = GetUIntLength(m_pReader, index, len);
+
+ if (result < 0) //error
+ return static_cast<long>(result);
+
+ if ((index + len) > stop)
+ return E_FILE_FORMAT_INVALID;
+
+ const long long idpos = index;
+ const long long id = ReadUInt(m_pReader, idpos, len);
+
+ if (id < 0) //error
+ return static_cast<long>(id);
+
+ index += len; //consume ID
+
+ //Read Size
+ result = GetUIntLength(m_pReader, index, len);
+
+ if (result < 0) //error
+ return static_cast<long>(result);
+
+ if ((index + len) > stop)
+ return E_FILE_FORMAT_INVALID;
+
+ const long long size = ReadUInt(m_pReader, index, len);
+
+ if (size < 0) //error
+ return static_cast<long>(size);
+
+ index += len; //consume length of size of element
+
+ if (id == 0x0F43B675) // Cluster ID
+ break;
+
+ if (id == 0x014D9B74) // SeekHead ID
+ {
+ ParseSeekHead(index, size, NULL);
+ break;
+ }
+ index += size;
+ }
+
+ if (m_clusterCount == 0)
+ return -1L;
+
+ while (m_pos < stop)
+ {
+ long long pos = m_pos;
+
+ long len;
+
+ long long result = GetUIntLength(m_pReader, pos, len);
+
+ if (result < 0) //error
+ return static_cast<long>(result);
+
+ if ((pos + len) > stop)
+ return E_FILE_FORMAT_INVALID;
+
+ const long long idpos = pos;
+ const long long id = ReadUInt(m_pReader, idpos, len);
+
+ if (id < 0) //error
+ return static_cast<long>(id);
+
+ pos += len; //consume ID
+
+ //Read Size
+ result = GetUIntLength(m_pReader, pos, len);
+
+ if (result < 0) //error
+ return static_cast<long>(result);
+
+ if ((pos + len) > stop)
+ return E_FILE_FORMAT_INVALID;
+
+ const long long size = ReadUInt(m_pReader, pos, len);
+
+ if (size < 0) //error
+ return static_cast<long>(size);
+
+ pos += len; //consume length of size of element
+
+ //Pos now points to start of payload
+
+ if ((pos + size) > stop)
+ return E_FILE_FORMAT_INVALID;
+
+ if (id == 0x0F43B675) //Cluster ID
+ break;
+
+ if (id == 0x014D9B74) //SeekHead ID
+ {
+ m_clusters = new Cluster*[m_clusterCount];
+ size_t index = 0;
+
+ ParseSeekHead(pos, size, &index);
+ assert(index == m_clusterCount);
+ }
+ else if (id == 0x0549A966) //Segment Info ID
+ {
+ assert(m_pInfo == NULL);
+ m_pInfo = new SegmentInfo(this, pos, size);
+ assert(m_pInfo); //TODO
+ }
+ else if (id == 0x0654AE6B) //Tracks ID
+ {
+ assert(m_pTracks == NULL);
+ m_pTracks = new Tracks(this, pos, size);
+ assert(m_pTracks); //TODO
+ }
+
+ m_pos = pos + size; //consume payload
+ }
+
+ assert(m_clusters);
+
+ //TODO: see notes above. This check is here (temporarily) to ensure
+ //that the first seekhead has entries for the clusters (because that's
+ //when they're loaded). In case we are given a file that lists the
+ //clusters in a second seekhead, the worst thing that happens is that
+ //we treat this as an invalid file (which is better then simply
+ //asserting somewhere). But that's only a work-around. What we need
+ //to do is be able to handle having multiple seekheads, and having
+ //clusters listed somewhere besides the first seekhead.
+ //
+ //if (m_clusters == NULL)
+ // return E_FILE_FORMAT_INVALID;
+
+ //NOTE: we stop parsing when we reach the first cluster, under the
+ //assumption all clusters are named in some SeekHead. Clusters
+ //will have been (pre)loaded, so we indicate that we have all clusters
+ //by adjusting the parse position:
+ m_pos = stop; //means "we have all clusters"
+
+ return 0L;
+}
+
+
+void Segment::ParseSeekHead(long long start, long long size_, size_t* pIndex)
+{
+ long long pos = start;
+ const long long stop = start + size_;
+ while (pos < stop)
+ {
+ long len;
+
+ const long long id = ReadUInt(m_pReader, pos, len);
+ assert(id >= 0); //TODO
+ assert((pos + len) <= stop);
+
+ pos += len; //consume ID
+
+ const long long size = ReadUInt(m_pReader, pos, len);
+ assert(size >= 0);
+ assert((pos + len) <= stop);
+
+ pos += len; //consume Size field
+ assert((pos + size) <= stop);
+
+ if (id == 0x0DBB) //SeekEntry ID
+ ParseSeekEntry(pos, size, pIndex);
+
+ pos += size; //consume payload
+ assert(pos <= stop);
+ }
+
+ assert(pos == stop);
+}
+
+
+void Segment::ParseSecondarySeekHead(long long off, size_t* pIndex)
+{
+ assert(off >= 0);
+ assert(off < m_size);
+
+ long long pos = m_start + off;
+ const long long stop = m_start + m_size;
+
+ long len;
+
+ long long result = GetUIntLength(m_pReader, pos, len);
+ assert(result == 0);
+ assert((pos + len) <= stop);
+
+ const long long idpos = pos;
+
+ const long long id = ReadUInt(m_pReader, idpos, len);
+ assert(id == 0x014D9B74); //SeekHead ID
+
+ pos += len; //consume ID
+ assert(pos < stop);
+
+ //Read Size
+
+ result = GetUIntLength(m_pReader, pos, len);
+ assert(result == 0);
+ assert((pos + len) <= stop);
+
+ const long long size = ReadUInt(m_pReader, pos, len);
+ assert(size >= 0);
+
+ pos += len; //consume length of size of element
+ assert((pos + size) <= stop);
+
+ //Pos now points to start of payload
+
+ ParseSeekHead(pos, size, pIndex);
+}
+
+
+void Segment::ParseSeekEntry(long long start, long long size_, size_t* pIndex)
+{
+ long long pos = start;
+
+ const long long stop = start + size_;
+
+ long len;
+
+ const long long seekIdId = ReadUInt(m_pReader, pos, len);
+ //seekIdId;
+ assert(seekIdId == 0x13AB); //SeekID ID
+ assert((pos + len) <= stop);
+
+ pos += len; //consume id
+
+ const long long seekIdSize = ReadUInt(m_pReader, pos, len);
+ assert(seekIdSize >= 0);
+ assert((pos + len) <= stop);
+
+ pos += len; //consume size
+
+ const long long seekId = ReadUInt(m_pReader, pos, len); //payload
+ assert(seekId >= 0);
+ assert(len == seekIdSize);
+ assert((pos + len) <= stop);
+
+ pos += seekIdSize; //consume payload
+
+ const long long seekPosId = ReadUInt(m_pReader, pos, len);
+ //seekPosId;
+ assert(seekPosId == 0x13AC); //SeekPos ID
+ assert((pos + len) <= stop);
+
+ pos += len; //consume id
+
+ const long long seekPosSize = ReadUInt(m_pReader, pos, len);
+ assert(seekPosSize >= 0);
+ assert((pos + len) <= stop);
+
+ pos += len; //consume size
+ assert((pos + seekPosSize) <= stop);
+
+ const long long seekOff = UnserializeUInt(m_pReader, pos, seekPosSize);
+ assert(seekOff >= 0);
+ assert(seekOff < m_size);
+
+ pos += seekPosSize; //consume payload
+ assert(pos == stop);
+
+ const long long seekPos = m_start + seekOff;
+ assert(seekPos < (m_start + m_size));
+
+ if (seekId == 0x0F43B675) //cluster id
+ {
+ if (pIndex == NULL)
+ ++m_clusterCount;
+ else
+ {
+ assert(m_clusters);
+ assert(m_clusterCount > 0);
+
+ size_t& index = *pIndex;
+ assert(index < m_clusterCount);
+
+ Cluster*& pCluster = m_clusters[index];
+
+ pCluster = Cluster::Parse(this, index, seekOff);
+ assert(pCluster); //TODO
+
+ ++index;
+ }
+ }
+ else if (seekId == 0x014D9B74) //SeekHead ID
+ {
+ ParseSecondarySeekHead(seekOff, pIndex);
+ }
+}
+
+
+long long Segment::Unparsed() const
+{
+ const long long stop = m_start + m_size;
+
+ const long long result = stop - m_pos;
+ assert(result >= 0);
+
+ return result;
+}
+
+
+#if 0 //NOTE: too inefficient
+long long Segment::Load(long long time_ns)
+{
+ if (Unparsed() <= 0)
+ return 0;
+
+ while (m_clusters.empty())
+ {
+ const long long result = Parse();
+
+ if (result) //error, or not enough bytes available
+ return result;
+
+ if (Unparsed() <= 0)
+ return 0;
+ }
+
+ while (m_clusters.back()->GetTime() < time_ns)
+ {
+ const long long result = Parse();
+
+ if (result) //error, or not enough bytes available
+ return result;
+
+ if (Unparsed() <= 0)
+ return 0;
+ }
+
+ return 0;
+}
+#endif
+
+
+Cluster* Segment::GetFirst()
+{
+ if ((m_clusters == NULL) || (m_clusterCount <= 0))
+ return &m_eos;
+
+ Cluster* const pCluster = m_clusters[0];
+ assert(pCluster);
+
+ return pCluster;
+}
+
+
+Cluster* Segment::GetLast()
+{
+ if ((m_clusters == NULL) || (m_clusterCount <= 0))
+ return &m_eos;
+
+ const size_t idx = m_clusterCount - 1;
+ Cluster* const pCluster = m_clusters[idx];
+ assert(pCluster);
+
+ return pCluster;
+}
+
+
+unsigned long Segment::GetCount() const
+{
+ //TODO: m_clusterCount should not be long long.
+ return static_cast<unsigned long>(m_clusterCount);
+}
+
+
+Cluster* Segment::GetNext(const Cluster* pCurr)
+{
+ assert(pCurr);
+ assert(pCurr != &m_eos);
+ assert(m_clusters);
+ assert(m_clusterCount > 0);
+
+ size_t idx = pCurr->m_index;
+ assert(idx < m_clusterCount);
+ assert(pCurr == m_clusters[idx]);
+
+ idx++;
+
+ if (idx >= m_clusterCount)
+ return &m_eos;
+
+ Cluster* const pNext = m_clusters[idx];
+ assert(pNext);
+
+ return pNext;
+}
+
+
+Cluster* Segment::GetCluster(long long time_ns)
+{
+ if ((m_clusters == NULL) || (m_clusterCount <= 0))
+ return &m_eos;
+
+ {
+ Cluster* const pCluster = m_clusters[0];
+ assert(pCluster);
+ assert(pCluster->m_index == 0);
+
+ if (time_ns <= pCluster->GetTime())
+ return pCluster;
+ }
+
+ //Binary search of cluster array
+
+ size_t i = 0;
+ size_t j = m_clusterCount;
+
+ while (i < j)
+ {
+ //INVARIANT:
+ //[0, i) <= time_ns
+ //[i, j) ?
+ //[j, m_clusterCount) > time_ns
+
+ const size_t k = i + (j - i) / 2;
+ assert(k < m_clusterCount);
+
+ Cluster* const pCluster = m_clusters[k];
+ assert(pCluster);
+ assert(pCluster->m_index == k);
+
+ const long long t = pCluster->GetTime();
+
+ if (t <= time_ns)
+ i = k + 1;
+ else
+ j = k;
+
+ assert(i <= j);
+ }
+
+ assert(i == j);
+ assert(i > 0);
+ assert(i <= m_clusterCount);
+
+ const size_t k = i - 1;
+
+ Cluster* const pCluster = m_clusters[k];
+ assert(pCluster);
+ assert(pCluster->m_index == k);
+ assert(pCluster->GetTime() <= time_ns);
+
+ return pCluster;
+}
+
+
+Tracks* Segment::GetTracks() const
+{
+ return m_pTracks;
+}
+
+
+const SegmentInfo* const Segment::GetInfo() const
+{
+ return m_pInfo;
+}
+
+
+long long Segment::GetDuration() const
+{
+ assert(m_pInfo);
+ return m_pInfo->GetDuration();
+}
+
+
+SegmentInfo::SegmentInfo(Segment* pSegment, long long start, long long size_) :
+ m_pSegment(pSegment),
+ m_start(start),
+ m_size(size_),
+ m_pMuxingAppAsUTF8(NULL),
+ m_pWritingAppAsUTF8(NULL),
+ m_pTitleAsUTF8(NULL)
+{
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ long long pos = start;
+ const long long stop = start + size_;
+
+ m_timecodeScale = 1000000;
+ m_duration = 0;
+
+
+ while (pos < stop)
+ {
+ if (Match(pReader, pos, 0x0AD7B1, m_timecodeScale))
+ assert(m_timecodeScale > 0);
+
+ else if (Match(pReader, pos, 0x0489, m_duration))
+ assert(m_duration >= 0);
+
+ else if (Match(pReader, pos, 0x0D80, m_pMuxingAppAsUTF8)) //[4D][80]
+ assert(m_pMuxingAppAsUTF8);
+
+ else if (Match(pReader, pos, 0x1741, m_pWritingAppAsUTF8)) //[57][41]
+ assert(m_pWritingAppAsUTF8);
+
+ else if (Match(pReader, pos, 0x3BA9, m_pTitleAsUTF8)) //[7B][A9]
+ assert(m_pTitleAsUTF8);
+
+ else
+ {
+ long len;
+
+ const long long id = ReadUInt(pReader, pos, len);
+ //id;
+ assert(id >= 0);
+ assert((pos + len) <= stop);
+
+ pos += len; //consume id
+ assert((stop - pos) > 0);
+
+ const long long size = ReadUInt(pReader, pos, len);
+ assert(size >= 0);
+ assert((pos + len) <= stop);
+
+ pos += len + size; //consume size and payload
+ assert(pos <= stop);
+ }
+ }
+
+ assert(pos == stop);
+}
+
+SegmentInfo::~SegmentInfo()
+{
+ if (m_pMuxingAppAsUTF8)
+ {
+ delete[] m_pMuxingAppAsUTF8;
+ m_pMuxingAppAsUTF8 = NULL;
+ }
+
+ if (m_pWritingAppAsUTF8)
+ {
+ delete[] m_pWritingAppAsUTF8;
+ m_pWritingAppAsUTF8 = NULL;
+ }
+
+ if (m_pTitleAsUTF8)
+ {
+ delete[] m_pTitleAsUTF8;
+ m_pTitleAsUTF8 = NULL;
+ }
+}
+
+long long SegmentInfo::GetTimeCodeScale() const
+{
+ return m_timecodeScale;
+}
+
+
+long long SegmentInfo::GetDuration() const
+{
+ assert(m_duration >= 0);
+ assert(m_timecodeScale >= 1);
+
+ const double dd = double(m_duration) * double(m_timecodeScale);
+ const long long d = static_cast<long long>(dd);
+
+ return d;
+}
+
+const char* SegmentInfo::GetMuxingAppAsUTF8() const
+{
+ return m_pMuxingAppAsUTF8;
+}
+
+const char* SegmentInfo::GetWritingAppAsUTF8() const
+{
+ return m_pWritingAppAsUTF8;
+}
+
+const char* SegmentInfo::GetTitleAsUTF8() const
+{
+ return m_pTitleAsUTF8;
+}
+
+Track::Track(Segment* pSegment, const Info& i) :
+ m_pSegment(pSegment),
+ m_info(i)
+{
+}
+
+Track::~Track()
+{
+ Info& info = const_cast<Info&>(m_info);
+ info.Clear();
+}
+
+Track::Info::Info():
+ type(-1),
+ number(-1),
+ uid(-1),
+ nameAsUTF8(NULL),
+ codecId(NULL),
+ codecPrivate(NULL),
+ codecPrivateSize(0),
+ codecNameAsUTF8(NULL)
+{
+}
+
+void Track::Info::Clear()
+{
+ delete[] nameAsUTF8;
+ nameAsUTF8 = NULL;
+
+ delete[] codecId;
+ codecId = NULL;
+
+ delete[] codecPrivate;
+ codecPrivate = NULL;
+
+ delete[] codecNameAsUTF8;
+ codecNameAsUTF8 = NULL;
+}
+
+const BlockEntry* Track::GetEOS() const
+{
+ return &m_eos;
+}
+
+long long Track::GetType() const
+{
+ const unsigned long result = static_cast<unsigned long>(m_info.type);
+ return result;
+}
+
+unsigned long Track::GetNumber() const
+{
+ assert(m_info.number >= 0);
+ const unsigned long result = static_cast<unsigned long>(m_info.number);
+ return result;
+}
+
+const char* Track::GetNameAsUTF8() const
+{
+ return m_info.nameAsUTF8;
+}
+
+const char* Track::GetCodecNameAsUTF8() const
+{
+ return m_info.codecNameAsUTF8;
+}
+
+
+const char* Track::GetCodecId() const
+{
+ return m_info.codecId;
+}
+
+
+const unsigned char* Track::GetCodecPrivate(size_t *optionalSize) const
+{
+ if (optionalSize) {
+ *optionalSize = m_info.codecPrivateSize;
+ }
+ return m_info.codecPrivate;
+}
+
+
+long Track::GetFirst(const BlockEntry*& pBlockEntry) const
+{
+ Cluster* const pCluster = m_pSegment->GetFirst();
+
+ //If Segment::GetFirst returns NULL, then this must be a network
+ //download, and we haven't loaded any clusters yet. In this case,
+ //returning NULL from Track::GetFirst means the same thing.
+
+ if ((pCluster == NULL) || pCluster->EOS())
+ {
+ pBlockEntry = NULL;
+ return E_BUFFER_NOT_FULL; //return 1L instead?
+ }
+
+ pBlockEntry = pCluster->GetFirst();
+
+ while (pBlockEntry)
+ {
+ const Block* const pBlock = pBlockEntry->GetBlock();
+ assert(pBlock);
+
+ if (pBlock->GetTrackNumber() == (unsigned long)m_info.number)
+ return 0L;
+
+ pBlockEntry = pCluster->GetNext(pBlockEntry);
+ }
+
+ //NOTE: if we get here, it means that we didn't find a block with
+ //a matching track number. We interpret that as an error (which
+ //might be too conservative).
+
+ pBlockEntry = GetEOS(); //so we can return a non-NULL value
+ return 1L;
+}
+
+
+long Track::GetNext(const BlockEntry* pCurrEntry, const BlockEntry*& pNextEntry) const
+{
+ assert(pCurrEntry);
+ assert(!pCurrEntry->EOS()); //?
+ assert(pCurrEntry->GetBlock()->GetTrackNumber() == (unsigned long)m_info.number);
+
+ const Cluster* const pCurrCluster = pCurrEntry->GetCluster();
+ assert(pCurrCluster);
+ assert(!pCurrCluster->EOS());
+
+ pNextEntry = pCurrCluster->GetNext(pCurrEntry);
+
+ while (pNextEntry)
+ {
+ const Block* const pNextBlock = pNextEntry->GetBlock();
+ assert(pNextBlock);
+
+ if (pNextBlock->GetTrackNumber() == (unsigned long)m_info.number)
+ return 0L;
+
+ pNextEntry = pCurrCluster->GetNext(pNextEntry);
+ }
+
+ Segment* pSegment = pCurrCluster->m_pSegment;
+ Cluster* const pNextCluster = pSegment->GetNext(pCurrCluster);
+
+ if ((pNextCluster == NULL) || pNextCluster->EOS())
+ {
+ if (pSegment->Unparsed() <= 0) //all clusters have been loaded
+ {
+ pNextEntry = GetEOS();
+ return 1L;
+ }
+
+ pNextEntry = NULL;
+ return E_BUFFER_NOT_FULL;
+ }
+
+ pNextEntry = pNextCluster->GetFirst();
+
+ while (pNextEntry)
+ {
+ const Block* const pNextBlock = pNextEntry->GetBlock();
+ assert(pNextBlock);
+
+ if (pNextBlock->GetTrackNumber() == (unsigned long)m_info.number)
+ return 0L;
+
+ pNextEntry = pNextCluster->GetNext(pNextEntry);
+ }
+
+ //TODO: what has happened here is that we did not find a block
+ //with a matching track number on the next cluster. It might
+ //be the case that some cluster beyond the next cluster
+ //contains a block having a matching track number, but for
+ //now we terminate the search immediately. We do this so that
+ //we don't end up searching the entire file looking for the
+ //next block. Another possibility is to try searching for the next
+ //block in a small, fixed number of clusters (intead searching
+ //just the next one), or to terminate the search when when the
+ //there is a large gap in time, or large gap in file position. It
+ //might very well be the case that the approach we use here is
+ //unnecessarily conservative.
+
+ //TODO: again, here's a case where we need to return the special
+ //EOS block. Or something. It's OK if pNext is NULL, because
+ //we only need it to set the stop time of the media sample.
+ //(The start time is determined from pCurr, which is non-NULL
+ //and non-EOS.) The problem is when we set pCurr=pNext; when
+ //pCurr has the value NULL we interpret that to mean that we
+ //haven't fully initialized pCurr and we attempt to set it to
+ //point to the first block for this track. But that's not what
+ //we want at all; we want the next call to PopulateSample to
+ //return end-of-stream, not (re)start from the beginning.
+ //
+ //One work-around is to send EOS immediately. We would send
+ //the EOS the next pass anyway, so maybe it's no great loss. The
+ //only problem is that if this the stream really does end one
+ //cluster early (relative to other tracks), or the last frame
+ //happens to be a keyframe ("CanSeekToEnd").
+ //
+ //The problem is that we need a way to mark as stream as
+ //"at end of stream" without actually being at end of stream.
+ //We need to give pCurr some value that means "you've reached EOS".
+ //We can't synthesize the special EOS Cluster immediately
+ //(when we first open the file, say), because we use the existance
+ //of that special cluster value to mean that we've read all of
+ //the clusters (this is a network download, so we can't know apriori
+ //how many we have).
+ //
+ //Or, we could return E_FAIL, and set another bit in the stream
+ //object itself, to indicate that it should send EOS earlier
+ //than when (pCurr=pStop).
+ //
+ //Or, probably the best solution, when we actually load the
+ //blocks into a cluster: if we notice that there's no block
+ //for a track, we synthesize a nonce EOS block for that track.
+ //That way we always have something to return. But that will
+ //only work for sequential scan???
+
+ //pNext = NULL;
+ //return E_FAIL;
+ pNextEntry = GetEOS();
+ return 1L;
+}
+
+
+Track::EOSBlock::EOSBlock()
+{
+}
+
+
+bool Track::EOSBlock::EOS() const
+{
+ return true;
+}
+
+
+Cluster* Track::EOSBlock::GetCluster() const
+{
+ return NULL;
+}
+
+
+size_t Track::EOSBlock::GetIndex() const
+{
+ return 0;
+}
+
+
+const Block* Track::EOSBlock::GetBlock() const
+{
+ return NULL;
+}
+
+
+bool Track::EOSBlock::IsBFrame() const
+{
+ return false;
+}
+
+
+VideoTrack::VideoTrack(Segment* pSegment, const Info& i) :
+ Track(pSegment, i),
+ m_width(-1),
+ m_height(-1),
+ m_rate(-1)
+{
+ assert(i.type == 1);
+ assert(i.number > 0);
+
+ IMkvReader* const pReader = pSegment->m_pReader;
+
+ const Settings& s = i.settings;
+ assert(s.start >= 0);
+ assert(s.size >= 0);
+
+ long long pos = s.start;
+ assert(pos >= 0);
+
+ const long long stop = pos + s.size;
+
+ while (pos < stop)
+ {
+#ifdef _DEBUG
+ long len;
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0); //TODO: handle error case
+ assert((pos + len) <= stop);
+#endif
+ if (Match(pReader, pos, 0x30, m_width))
+ ;
+ else if (Match(pReader, pos, 0x3A, m_height))
+ ;
+ else if (Match(pReader, pos, 0x0383E3, m_rate))
+ ;
+ else
+ {
+ long len;
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0); //TODO: handle error case
+ assert((pos + len) <= stop);
+
+ pos += len; //consume id
+
+ const long long size = ReadUInt(pReader, pos, len);
+ assert(size >= 0); //TODO: handle error case
+ assert((pos + len) <= stop);
+
+ pos += len; //consume length of size
+ assert((pos + size) <= stop);
+
+ //pos now designates start of payload
+
+ pos += size; //consume payload
+ assert(pos <= stop);
+ }
+ }
+
+ return;
+}
+
+
+bool VideoTrack::VetEntry(const BlockEntry* pBlockEntry) const
+{
+ assert(pBlockEntry);
+
+ const Block* const pBlock = pBlockEntry->GetBlock();
+ assert(pBlock);
+ assert(pBlock->GetTrackNumber() == (unsigned long)m_info.number);
+
+ return pBlock->IsKey();
+}
+
+
+
+long long VideoTrack::GetWidth() const
+{
+ return m_width;
+}
+
+
+long long VideoTrack::GetHeight() const
+{
+ return m_height;
+}
+
+
+double VideoTrack::GetFrameRate() const
+{
+ return m_rate;
+}
+
+
+AudioTrack::AudioTrack(Segment* pSegment, const Info& i) :
+ Track(pSegment, i)
+{
+ assert(i.type == 2);
+ assert(i.number > 0);
+
+ IMkvReader* const pReader = pSegment->m_pReader;
+
+ const Settings& s = i.settings;
+ assert(s.start >= 0);
+ assert(s.size >= 0);
+
+ long long pos = s.start;
+ assert(pos >= 0);
+
+ const long long stop = pos + s.size;
+
+ while (pos < stop)
+ {
+#ifdef _DEBUG
+ long len;
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0); //TODO: handle error case
+ assert((pos + len) <= stop);
+#endif
+ if (Match(pReader, pos, 0x35, m_rate))
+ ;
+ else if (Match(pReader, pos, 0x1F, m_channels))
+ ;
+ else if (Match(pReader, pos, 0x2264, m_bitDepth))
+ ;
+ else
+ {
+ long len;
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0); //TODO: handle error case
+ assert((pos + len) <= stop);
+
+ pos += len; //consume id
+
+ const long long size = ReadUInt(pReader, pos, len);
+ assert(size >= 0); //TODO: handle error case
+ assert((pos + len) <= stop);
+
+ pos += len; //consume length of size
+ assert((pos + size) <= stop);
+
+ //pos now designates start of payload
+
+ pos += size; //consume payload
+ assert(pos <= stop);
+ }
+ }
+
+ return;
+}
+
+bool AudioTrack::VetEntry(const BlockEntry* pBlockEntry) const
+{
+ assert(pBlockEntry);
+
+ const Block* const pBlock = pBlockEntry->GetBlock();
+ assert(pBlock);
+ assert(pBlock->GetTrackNumber() == (unsigned long)m_info.number);
+
+ return true;
+}
+
+
+double AudioTrack::GetSamplingRate() const
+{
+ return m_rate;
+}
+
+
+long long AudioTrack::GetChannels() const
+{
+ return m_channels;
+}
+
+long long AudioTrack::GetBitDepth() const
+{
+ return m_bitDepth;
+}
+
+Tracks::Tracks(Segment* pSegment, long long start, long long size_) :
+ m_pSegment(pSegment),
+ m_start(start),
+ m_size(size_),
+ m_trackEntries(NULL),
+ m_trackEntriesEnd(NULL)
+{
+ long long stop = m_start + m_size;
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ long long pos1 = m_start;
+ int count = 0;
+
+ while (pos1 < stop)
+ {
+ long len;
+ const long long id = ReadUInt(pReader, pos1, len);
+ assert(id >= 0);
+ assert((pos1 + len) <= stop);
+
+ pos1 += len; //consume id
+
+ const long long size = ReadUInt(pReader, pos1, len);
+ assert(size >= 0);
+ assert((pos1 + len) <= stop);
+
+ pos1 += len; //consume length of size
+
+ //pos now desinates start of element
+ if (id == 0x2E) //TrackEntry ID
+ ++count;
+
+ pos1 += size; //consume payload
+ assert(pos1 <= stop);
+ }
+
+ if (count <= 0)
+ return;
+
+ m_trackEntries = new Track*[count];
+ m_trackEntriesEnd = m_trackEntries;
+
+ long long pos = m_start;
+
+ while (pos < stop)
+ {
+ long len;
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0);
+ assert((pos + len) <= stop);
+
+ pos += len; //consume id
+
+ const long long size1 = ReadUInt(pReader, pos, len);
+ assert(size1 >= 0);
+ assert((pos + len) <= stop);
+
+ pos += len; //consume length of size
+
+ //pos now desinates start of element
+
+ if (id == 0x2E) //TrackEntry ID
+ ParseTrackEntry(pos, size1, *m_trackEntriesEnd++);
+
+ pos += size1; //consume payload
+ assert(pos <= stop);
+ }
+}
+
+unsigned long Tracks::GetTracksCount() const
+{
+ const ptrdiff_t result = m_trackEntriesEnd - m_trackEntries;
+ assert(result >= 0);
+
+ return static_cast<unsigned long>(result);
+}
+
+
+void Tracks::ParseTrackEntry(
+ long long start,
+ long long size,
+ Track*& pTrack)
+{
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ long long pos = start;
+ const long long stop = start + size;
+
+ Track::Info i;
+
+ Track::Settings videoSettings;
+ videoSettings.start = -1;
+
+ Track::Settings audioSettings;
+ audioSettings.start = -1;
+
+ while (pos < stop)
+ {
+#ifdef _DEBUG
+ long len;
+ const long long id = ReadUInt(pReader, pos, len);
+ len;
+ id;
+#endif
+ if (Match(pReader, pos, 0x57, i.number))
+ assert(i.number > 0);
+
+ else if (Match(pReader, pos, 0x33C5, i.uid))
+ ;
+
+ else if (Match(pReader, pos, 0x03, i.type))
+ ;
+
+ else if (Match(pReader, pos, 0x136E, i.nameAsUTF8))
+ assert(i.nameAsUTF8);
+
+ else if (Match(pReader, pos, 0x06, i.codecId))
+ ;
+
+ else if (Match(pReader, pos, 0x23A2, i.codecPrivate, &i.codecPrivateSize))
+ ;
+
+ else if (Match(pReader, pos, 0x058688, i.codecNameAsUTF8))
+ assert(i.codecNameAsUTF8);
+
+ else
+ {
+ long len;
+
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0); //TODO: handle error case
+ assert((pos + len) <= stop);
+
+ pos += len; //consume id
+
+ const long long size = ReadUInt(pReader, pos, len);
+ assert(size >= 0); //TODO: handle error case
+ assert((pos + len) <= stop);
+
+ pos += len; //consume length of size
+ const long long start = pos;
+
+ pos += size; //consume payload
+ assert(pos <= stop);
+
+ if (id == 0x60)
+ {
+ videoSettings.start = start;
+ videoSettings.size = size;
+ }
+ else if (id == 0x61)
+ {
+ audioSettings.start = start;
+ audioSettings.size = size;
+ }
+ }
+ }
+
+ assert(pos == stop);
+ //TODO: propertly vet info.number, to ensure both its existence,
+ //and that it is unique among all tracks.
+ assert(i.number > 0);
+
+ //TODO: vet settings, to ensure that video settings (0x60)
+ //were specified when type = 1, and that audio settings (0x61)
+ //were specified when type = 2.
+ if (i.type == 1) //video
+ {
+ assert(audioSettings.start < 0);
+ assert(videoSettings.start >= 0);
+
+ i.settings = videoSettings;
+
+ VideoTrack* const t = new VideoTrack(m_pSegment, i);
+ assert(t); //TODO
+ pTrack = t;
+ }
+ else if (i.type == 2) //audio
+ {
+ assert(videoSettings.start < 0);
+ assert(audioSettings.start >= 0);
+
+ i.settings = audioSettings;
+
+ AudioTrack* const t = new AudioTrack(m_pSegment, i);
+ assert(t); //TODO
+ pTrack = t;
+ }
+ else
+ {
+ // for now we do not support other track types yet.
+ // TODO: support other track types
+ i.Clear();
+
+ pTrack = NULL;
+ }
+
+ return;
+}
+
+
+Tracks::~Tracks()
+{
+ Track** i = m_trackEntries;
+ Track** const j = m_trackEntriesEnd;
+
+ while (i != j)
+ {
+ Track* pTrack = *i++;
+ delete pTrack;
+ pTrack = NULL;
+ }
+
+ delete[] m_trackEntries;
+}
+
+
+Track* Tracks::GetTrackByNumber(unsigned long tn) const
+{
+ Track** i = m_trackEntries;
+ Track** const j = m_trackEntriesEnd;
+
+ while (i != j)
+ {
+ Track* const pTrack = *i++;
+
+ if (pTrack == NULL)
+ continue;
+
+ if (tn == pTrack->GetNumber())
+ return pTrack;
+ }
+
+ return NULL; //not found
+}
+
+
+Track* Tracks::GetTrackByIndex(unsigned long idx) const
+{
+ const ptrdiff_t count = m_trackEntriesEnd - m_trackEntries;
+
+ if (idx >= static_cast<unsigned long>(count))
+ return NULL;
+
+ return m_trackEntries[idx];
+}
+
+
+void Cluster::Load()
+{
+ assert(m_pSegment);
+
+ if (m_start > 0)
+ {
+ assert(m_size > 0);
+ assert(m_timecode >= 0);
+ return;
+ }
+
+ assert(m_size == 0);
+ assert(m_timecode < 0);
+
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ const long long off = -m_start; //relative to segment
+ long long pos = m_pSegment->m_start + off; //absolute
+
+ long len;
+
+ const long long id_ = ReadUInt(pReader, pos, len);
+ assert(id_ >= 0);
+ assert(id_ == 0x0F43B675); //Cluster ID
+
+ pos += len; //consume id
+
+ const long long size_ = ReadUInt(pReader, pos, len);
+ assert(size_ >= 0);
+
+ pos += len; //consume size
+
+ m_start = pos;
+ m_size = size_;
+
+ const long long stop = m_start + size_;
+
+ long long timecode = -1;
+
+ while (pos < stop)
+ {
+ if (Match(pReader, pos, 0x67, timecode))
+ break;
+ else
+ {
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0); //TODO
+ assert((pos + len) <= stop);
+
+ pos += len; //consume id
+
+ const long long size = ReadUInt(pReader, pos, len);
+ assert(size >= 0); //TODO
+ assert((pos + len) <= stop);
+
+ pos += len; //consume size
+
+ if (id == 0x20) //BlockGroup ID
+ break;
+
+ if (id == 0x23) //SimpleBlock ID
+ break;
+
+ pos += size; //consume payload
+ assert(pos <= stop);
+ }
+ }
+
+ assert(pos <= stop);
+ assert(timecode >= 0);
+
+ m_timecode = timecode;
+}
+
+
+Cluster* Cluster::Parse(
+ Segment* pSegment,
+ size_t idx,
+ long long off)
+{
+ assert(pSegment);
+ assert(off >= 0);
+ assert(off < pSegment->m_size);
+ Cluster* const pCluster = new Cluster(pSegment, idx, -off);
+ assert(pCluster);
+
+ return pCluster;
+}
+
+
+Cluster::Cluster() :
+ m_pSegment(NULL),
+ m_index(0),
+ m_start(0),
+ m_size(0),
+ m_timecode(0),
+ m_pEntries(NULL),
+ m_entriesCount(0)
+{
+}
+
+Cluster::Cluster(
+ Segment* pSegment,
+ size_t idx,
+ long long off) :
+ m_pSegment(pSegment),
+ m_index(idx),
+ m_start(off),
+ m_size(0),
+ m_timecode(-1),
+ m_pEntries(NULL),
+ m_entriesCount(0)
+{
+}
+
+
+Cluster::~Cluster()
+{
+#if 0
+ while (!m_pEntries.empty())
+ {
+ BlockEntry* pBlockEntry = m_pEntries.front();
+ assert(pBlockEntry);
+
+ m_pEntries.pop_front();
+ delete pBlockEntry;
+ }
+#else
+ BlockEntry** i = m_pEntries;
+ BlockEntry** const j = m_pEntries + m_entriesCount;
+ while (i != j)
+ {
+ BlockEntry* p = *i++;
+
+ assert(p);
+ delete p;
+ }
+
+ delete[] m_pEntries;
+#endif
+
+}
+
+bool Cluster::EOS() const
+{
+ return (m_pSegment == 0);
+}
+
+
+void Cluster::LoadBlockEntries()
+{
+ if (m_pEntries)
+ return;
+
+ Load();
+ assert(m_timecode >= 0);
+ assert(m_start > 0);
+ assert(m_size > 0);
+
+ IMkvReader* const pReader = m_pSegment->m_pReader;
+
+ long long pos = m_start;
+ const long long stop = m_start + m_size;
+ long long timecode = -1;
+
+ long long idx = pos;
+
+ m_entriesCount = 0;
+
+ while (idx < stop)
+ {
+ if (Match(pReader, idx, 0x67, timecode))
+ assert(timecode == m_timecode);
+ else
+ {
+ long len;
+
+ const long long id = ReadUInt(pReader, idx, len);
+ assert(id >= 0); //TODO
+ assert((idx + len) <= stop);
+
+ idx += len; //consume id
+
+ const long long size = ReadUInt(pReader, idx, len);
+ assert(size >= 0); //TODO
+ assert((idx + len) <= stop);
+
+ idx += len; //consume size
+
+ if (id == 0x20) //BlockGroup ID
+ ++m_entriesCount;
+ else if (id == 0x23) //SimpleBlock ID
+ ++m_entriesCount;
+
+ idx += size; //consume payload
+
+ assert(idx <= stop);
+ }
+ }
+
+ if (m_entriesCount == 0)
+ return;
+
+ m_pEntries = new BlockEntry*[m_entriesCount];
+ size_t index = 0;
+
+ while (pos < stop)
+ {
+ if (Match(pReader, pos, 0x67, timecode))
+ assert(timecode == m_timecode);
+ else
+ {
+ long len;
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0); //TODO
+ assert((pos + len) <= stop);
+
+ pos += len; //consume id
+
+ const long long size = ReadUInt(pReader, pos, len);
+ assert(size >= 0); //TODO
+ assert((pos + len) <= stop);
+
+ pos += len; //consume size
+
+ if (id == 0x20) //BlockGroup ID
+ ParseBlockGroup(pos, size, index++);
+ else if (id == 0x23) //SimpleBlock ID
+ ParseSimpleBlock(pos, size, index++);
+
+ pos += size; //consume payload
+ assert(pos <= stop);
+ }
+ }
+
+ assert(pos == stop);
+ assert(timecode >= 0);
+ assert(index == m_entriesCount);
+}
+
+
+
+long long Cluster::GetTimeCode()
+{
+ Load();
+ return m_timecode;
+}
+
+
+long long Cluster::GetTime()
+{
+ const long long tc = GetTimeCode();
+ assert(tc >= 0);
+
+ const SegmentInfo* const pInfo = m_pSegment->GetInfo();
+ assert(pInfo);
+
+ const long long scale = pInfo->GetTimeCodeScale();
+ assert(scale >= 1);
+
+ const long long t = m_timecode * scale;
+
+ return t;
+}
+
+
+void Cluster::ParseBlockGroup(long long start, long long size, size_t index)
+{
+ assert(m_pEntries);
+ assert(m_entriesCount);
+ assert(index < m_entriesCount);
+
+ BlockGroup* const pGroup = new BlockGroup(this, index, start, size);
+ assert(pGroup); //TODO
+
+ m_pEntries[index] = pGroup;
+}
+
+
+
+void Cluster::ParseSimpleBlock(long long start, long long size, size_t index)
+{
+ assert(m_pEntries);
+ assert(m_entriesCount);
+ assert(index < m_entriesCount);
+
+ SimpleBlock* const pSimpleBlock = new SimpleBlock(this, index, start, size);
+ assert(pSimpleBlock); //TODO
+
+ m_pEntries[index] = pSimpleBlock;
+}
+
+
+const BlockEntry* Cluster::GetFirst()
+{
+ LoadBlockEntries();
+
+ return m_pEntries[0];
+}
+
+
+const BlockEntry* Cluster::GetLast()
+{
+ if (m_entriesCount == 0)
+ return m_pEntries[0];
+
+ return m_pEntries[m_entriesCount-1];
+}
+
+
+const BlockEntry* Cluster::GetNext(const BlockEntry* pEntry) const
+{
+ assert(pEntry);
+
+ size_t idx = pEntry->GetIndex();
+
+ ++idx;
+
+ if (idx == m_entriesCount)
+ return NULL;
+
+ return m_pEntries[idx];
+
+}
+
+
+const BlockEntry* Cluster::GetEntry(const Track* pTrack)
+{
+
+ assert(pTrack);
+
+ if (m_pSegment == NULL) //EOS
+ return pTrack->GetEOS();
+
+ LoadBlockEntries();
+
+ BlockEntry* i = *m_pEntries;
+ BlockEntry* j = *m_pEntries + m_entriesCount;
+ while (i != j)
+ {
+ BlockEntry* pEntry = i;
+ i++;
+ assert(pEntry);
+ assert(!pEntry->EOS());
+
+ const Block* const pBlock = pEntry->GetBlock();
+ assert(pBlock);
+
+ if (pBlock->GetTrackNumber() != pTrack->GetNumber())
+ continue;
+
+ if (pTrack->VetEntry(pEntry))
+ return pEntry;
+ }
+
+ return pTrack->GetEOS(); //no satisfactory block found
+}
+
+
+BlockEntry::BlockEntry()
+{
+}
+
+
+BlockEntry::~BlockEntry()
+{
+}
+
+
+
+SimpleBlock::SimpleBlock(
+ Cluster* pCluster,
+ size_t idx,
+ long long start,
+ long long size) :
+ m_pCluster(pCluster),
+ m_index(idx),
+ m_block(start, size, pCluster->m_pSegment->m_pReader)
+{
+}
+
+
+bool SimpleBlock::EOS() const
+{
+ return false;
+}
+
+
+Cluster* SimpleBlock::GetCluster() const
+{
+ return m_pCluster;
+}
+
+
+size_t SimpleBlock::GetIndex() const
+{
+ return m_index;
+}
+
+
+const Block* SimpleBlock::GetBlock() const
+{
+ return &m_block;
+}
+
+
+bool SimpleBlock::IsBFrame() const
+{
+ return false;
+}
+
+
+BlockGroup::BlockGroup(
+ Cluster* pCluster,
+ size_t idx,
+ long long start,
+ long long size_) :
+ m_pCluster(pCluster),
+ m_index(idx),
+ m_prevTimeCode(0),
+ m_nextTimeCode(0),
+ m_pBlock(NULL) //TODO: accept multiple blocks within a block group
+{
+ IMkvReader* const pReader = m_pCluster->m_pSegment->m_pReader;
+
+ long long pos = start;
+ const long long stop = start + size_;
+
+ bool bSimpleBlock = false;
+
+ while (pos < stop)
+ {
+ short t;
+
+ if (Match(pReader, pos, 0x7B, t))
+ {
+ if (t < 0)
+ m_prevTimeCode = t;
+ else if (t > 0)
+ m_nextTimeCode = t;
+ else
+ assert(false);
+ }
+ else
+ {
+ long len;
+ const long long id = ReadUInt(pReader, pos, len);
+ assert(id >= 0); //TODO
+ assert((pos + len) <= stop);
+
+ pos += len; //consume ID
+
+ const long long size = ReadUInt(pReader, pos, len);
+ assert(size >= 0); //TODO
+ assert((pos + len) <= stop);
+
+ pos += len; //consume size
+
+ switch (id)
+ {
+ case 0x23: //SimpleBlock ID
+ bSimpleBlock = true;
+ //YES, FALL THROUGH TO NEXT CASE
+
+ case 0x21: //Block ID
+ ParseBlock(pos, size);
+ break;
+
+ default:
+ break;
+ }
+
+ pos += size; //consume payload
+ assert(pos <= stop);
+ }
+ }
+
+ assert(pos == stop);
+ assert(m_pBlock);
+
+ if (!bSimpleBlock)
+ m_pBlock->SetKey(m_prevTimeCode >= 0);
+}
+
+
+BlockGroup::~BlockGroup()
+{
+ delete m_pBlock;
+}
+
+
+void BlockGroup::ParseBlock(long long start, long long size)
+{
+ IMkvReader* const pReader = m_pCluster->m_pSegment->m_pReader;
+
+ Block* const pBlock = new Block(start, size, pReader);
+ assert(pBlock); //TODO
+
+ //TODO: the Matroska spec says you have multiple blocks within the
+ //same block group, with blocks ranked by priority (the flag bits).
+ //I haven't ever seen such a file (mkvmux certainly doesn't make
+ //one), so until then I'll just assume block groups contain a single
+ //block.
+#if 0
+ m_blocks.push_back(pBlock);
+#else
+ assert(m_pBlock == NULL);
+ m_pBlock = pBlock;
+#endif
+
+#if 0
+ Track* const pTrack = pBlock->GetTrack();
+ assert(pTrack);
+
+ pTrack->Insert(pBlock);
+#endif
+}
+
+
+bool BlockGroup::EOS() const
+{
+ return false;
+}
+
+
+Cluster* BlockGroup::GetCluster() const
+{
+ return m_pCluster;
+}
+
+
+size_t BlockGroup::GetIndex() const
+{
+ return m_index;
+}
+
+
+const Block* BlockGroup::GetBlock() const
+{
+ return m_pBlock;
+}
+
+
+short BlockGroup::GetPrevTimeCode() const
+{
+ return m_prevTimeCode;
+}
+
+
+short BlockGroup::GetNextTimeCode() const
+{
+ return m_nextTimeCode;
+}
+
+
+bool BlockGroup::IsBFrame() const
+{
+ return (m_nextTimeCode > 0);
+}
+
+
+
+Block::Block(long long start, long long size_, IMkvReader* pReader) :
+ m_start(start),
+ m_size(size_)
+{
+ long long pos = start;
+ const long long stop = start + size_;
+
+ long len;
+
+ m_track = ReadUInt(pReader, pos, len);
+ assert(m_track > 0);
+ assert((pos + len) <= stop);
+
+ pos += len; //consume track number
+ assert((stop - pos) >= 2);
+
+ m_timecode = Unserialize2SInt(pReader, pos);
+
+ pos += 2;
+ assert((stop - pos) >= 1);
+
+ const long hr = pReader->Read(pos, 1, &m_flags);
+ assert(hr == 0L);
+
+ ++pos;
+ assert(pos <= stop);
+
+ m_frameOff = pos;
+
+ const long long frame_size = stop - pos;
+
+ assert(frame_size <= 2147483647L);
+
+ m_frameSize = static_cast<long>(frame_size);
+}
+
+
+long long Block::GetTimeCode(Cluster* pCluster) const
+{
+ assert(pCluster);
+
+ const long long tc0 = pCluster->GetTimeCode();
+ assert(tc0 >= 0);
+
+ const long long tc = tc0 + static_cast<long long>(m_timecode);
+ assert(tc >= 0);
+
+ return tc; //unscaled timecode units
+}
+
+
+long long Block::GetTime(Cluster* pCluster) const
+{
+ assert(pCluster);
+
+ const long long tc = GetTimeCode(pCluster);
+
+ const Segment* const pSegment = pCluster->m_pSegment;
+ const SegmentInfo* const pInfo = pSegment->GetInfo();
+ assert(pInfo);
+
+ const long long scale = pInfo->GetTimeCodeScale();
+ assert(scale >= 1);
+
+ const long long ns = tc * scale;
+
+ return ns;
+}
+
+
+unsigned long Block::GetTrackNumber() const
+{
+ assert(m_track > 0);
+
+ return static_cast<unsigned long>(m_track);
+}
+
+
+bool Block::IsKey() const
+{
+ return ((m_flags & static_cast<unsigned char>(1 << 7)) != 0);
+}
+
+
+void Block::SetKey(bool bKey)
+{
+ if (bKey)
+ m_flags |= static_cast<unsigned char>(1 << 7);
+ else
+ m_flags &= 0x7F;
+}
+
+
+long Block::GetSize() const
+{
+ return m_frameSize;
+}
+
+
+long Block::Read(IMkvReader* pReader, unsigned char* buf) const
+{
+
+ assert(pReader);
+ assert(buf);
+
+ const long hr = pReader->Read(m_frameOff, m_frameSize, buf);
+
+ return hr;
+}
+
+
+} //end namespace mkvparser
diff --git a/media/libstagefright/matroska/mkvparser.hpp b/media/libstagefright/matroska/mkvparser.hpp
new file mode 100644
index 0000000..4d311b4
--- /dev/null
+++ b/media/libstagefright/matroska/mkvparser.hpp
@@ -0,0 +1,428 @@
+#ifndef MKVPARSER_HPP
+#define MKVPARSER_HPP
+
+#include <cstdlib>
+#include <cstdio>
+
+namespace mkvparser
+{
+
+const int E_FILE_FORMAT_INVALID = -2;
+const int E_BUFFER_NOT_FULL = -3;
+
+class IMkvReader
+{
+public:
+ virtual int Read(long long position, long length, unsigned char* buffer) = 0;
+ virtual int Length(long long* total, long long* available) = 0;
+protected:
+ virtual ~IMkvReader();
+};
+
+long long GetUIntLength(IMkvReader*, long long, long&);
+long long ReadUInt(IMkvReader*, long long, long&);
+long long SyncReadUInt(IMkvReader*, long long pos, long long stop, long&);
+long long UnserializeUInt(IMkvReader*, long long pos, long long size);
+float Unserialize4Float(IMkvReader*, long long);
+double Unserialize8Double(IMkvReader*, long long);
+short Unserialize2SInt(IMkvReader*, long long);
+signed char Unserialize1SInt(IMkvReader*, long long);
+bool Match(IMkvReader*, long long&, unsigned long, long long&);
+bool Match(IMkvReader*, long long&, unsigned long, char*&);
+bool Match(IMkvReader*, long long&, unsigned long,unsigned char*&,
+ size_t *optionalSize = NULL);
+bool Match(IMkvReader*, long long&, unsigned long, double&);
+bool Match(IMkvReader*, long long&, unsigned long, short&);
+
+
+struct EBMLHeader
+{
+ EBMLHeader();
+ ~EBMLHeader();
+ long long m_version;
+ long long m_readVersion;
+ long long m_maxIdLength;
+ long long m_maxSizeLength;
+ char* m_docType;
+ long long m_docTypeVersion;
+ long long m_docTypeReadVersion;
+
+ long long Parse(IMkvReader*, long long&);
+};
+
+
+class Segment;
+class Track;
+class Cluster;
+
+class Block
+{
+ Block(const Block&);
+ Block& operator=(const Block&);
+
+public:
+ const long long m_start;
+ const long long m_size;
+
+ Block(long long start, long long size, IMkvReader*);
+
+ unsigned long GetTrackNumber() const;
+
+ long long GetTimeCode(Cluster*) const; //absolute, but not scaled
+ long long GetTime(Cluster*) const; //absolute, and scaled (nanosecond units)
+ bool IsKey() const;
+ void SetKey(bool);
+
+ long GetSize() const;
+ long Read(IMkvReader*, unsigned char*) const;
+
+private:
+ long long m_track; //Track::Number()
+ short m_timecode; //relative to cluster
+ unsigned char m_flags;
+ long long m_frameOff;
+ long m_frameSize;
+
+};
+
+
+class BlockEntry
+{
+ BlockEntry(const BlockEntry&);
+ BlockEntry& operator=(const BlockEntry&);
+
+public:
+ virtual ~BlockEntry();
+ virtual bool EOS() const = 0;
+ virtual Cluster* GetCluster() const = 0;
+ virtual size_t GetIndex() const = 0;
+ virtual const Block* GetBlock() const = 0;
+ virtual bool IsBFrame() const = 0;
+
+protected:
+ BlockEntry();
+
+};
+
+
+class SimpleBlock : public BlockEntry
+{
+ SimpleBlock(const SimpleBlock&);
+ SimpleBlock& operator=(const SimpleBlock&);
+
+public:
+ SimpleBlock(Cluster*, size_t, long long start, long long size);
+
+ bool EOS() const;
+ Cluster* GetCluster() const;
+ size_t GetIndex() const;
+ const Block* GetBlock() const;
+ bool IsBFrame() const;
+
+protected:
+ Cluster* const m_pCluster;
+ const size_t m_index;
+ Block m_block;
+
+};
+
+
+class BlockGroup : public BlockEntry
+{
+ BlockGroup(const BlockGroup&);
+ BlockGroup& operator=(const BlockGroup&);
+
+public:
+ BlockGroup(Cluster*, size_t, long long, long long);
+ ~BlockGroup();
+
+ bool EOS() const;
+ Cluster* GetCluster() const;
+ size_t GetIndex() const;
+ const Block* GetBlock() const;
+ bool IsBFrame() const;
+
+ short GetPrevTimeCode() const; //relative to block's time
+ short GetNextTimeCode() const; //as above
+
+protected:
+ Cluster* const m_pCluster;
+ const size_t m_index;
+
+private:
+ BlockGroup(Cluster*, size_t, unsigned long);
+ void ParseBlock(long long start, long long size);
+
+ short m_prevTimeCode;
+ short m_nextTimeCode;
+
+ //TODO: the Matroska spec says you can have multiple blocks within the
+ //same block group, with blocks ranked by priority (the flag bits).
+ //For now we just cache a single block.
+#if 0
+ typedef std::deque<Block*> blocks_t;
+ blocks_t m_blocks; //In practice should contain only a single element.
+#else
+ Block* m_pBlock;
+#endif
+
+};
+
+
+class Track
+{
+ Track(const Track&);
+ Track& operator=(const Track&);
+
+public:
+ Segment* const m_pSegment;
+ virtual ~Track();
+
+ long long GetType() const;
+ unsigned long GetNumber() const;
+ const char* GetNameAsUTF8() const;
+ const char* GetCodecNameAsUTF8() const;
+ const char* GetCodecId() const;
+ const unsigned char* GetCodecPrivate(
+ size_t *optionalSize = NULL) const;
+
+ const BlockEntry* GetEOS() const;
+
+ struct Settings
+ {
+ long long start;
+ long long size;
+ };
+
+ struct Info
+ {
+ long long type;
+ long long number;
+ long long uid;
+ char* nameAsUTF8;
+ char* codecId;
+ unsigned char* codecPrivate;
+ size_t codecPrivateSize;
+ char* codecNameAsUTF8;
+ Settings settings;
+ Info();
+ void Clear();
+ };
+
+ long GetFirst(const BlockEntry*&) const;
+ long GetNext(const BlockEntry* pCurr, const BlockEntry*& pNext) const;
+ virtual bool VetEntry(const BlockEntry*) const = 0;
+
+protected:
+ Track(Segment*, const Info&);
+ const Info m_info;
+
+ class EOSBlock : public BlockEntry
+ {
+ public:
+ EOSBlock();
+
+ bool EOS() const;
+ Cluster* GetCluster() const;
+ size_t GetIndex() const;
+ const Block* GetBlock() const;
+ bool IsBFrame() const;
+ };
+
+ EOSBlock m_eos;
+
+};
+
+
+class VideoTrack : public Track
+{
+ VideoTrack(const VideoTrack&);
+ VideoTrack& operator=(const VideoTrack&);
+
+public:
+ VideoTrack(Segment*, const Info&);
+ long long GetWidth() const;
+ long long GetHeight() const;
+ double GetFrameRate() const;
+
+ bool VetEntry(const BlockEntry*) const;
+
+private:
+ long long m_width;
+ long long m_height;
+ double m_rate;
+
+};
+
+
+class AudioTrack : public Track
+{
+ AudioTrack(const AudioTrack&);
+ AudioTrack& operator=(const AudioTrack&);
+
+public:
+ AudioTrack(Segment*, const Info&);
+ double GetSamplingRate() const;
+ long long GetChannels() const;
+ long long GetBitDepth() const;
+ bool VetEntry(const BlockEntry*) const;
+
+private:
+ double m_rate;
+ long long m_channels;
+ long long m_bitDepth;
+};
+
+
+class Tracks
+{
+ Tracks(const Tracks&);
+ Tracks& operator=(const Tracks&);
+
+public:
+ Segment* const m_pSegment;
+ const long long m_start;
+ const long long m_size;
+
+ Tracks(Segment*, long long start, long long size);
+ virtual ~Tracks();
+
+ Track* GetTrackByNumber(unsigned long tn) const;
+ Track* GetTrackByIndex(unsigned long idx) const;
+
+private:
+ Track** m_trackEntries;
+ Track** m_trackEntriesEnd;
+
+ void ParseTrackEntry(long long, long long, Track*&);
+
+public:
+ unsigned long GetTracksCount() const;
+};
+
+
+class SegmentInfo
+{
+ SegmentInfo(const SegmentInfo&);
+ SegmentInfo& operator=(const SegmentInfo&);
+
+public:
+ Segment* const m_pSegment;
+ const long long m_start;
+ const long long m_size;
+
+ SegmentInfo(Segment*, long long start, long long size);
+ ~SegmentInfo();
+ long long GetTimeCodeScale() const;
+ long long GetDuration() const; //scaled
+ const char* GetMuxingAppAsUTF8() const;
+ const char* GetWritingAppAsUTF8() const;
+ const char* GetTitleAsUTF8() const;
+
+private:
+ long long m_timecodeScale;
+ double m_duration;
+ char* m_pMuxingAppAsUTF8;
+ char* m_pWritingAppAsUTF8;
+ char* m_pTitleAsUTF8;
+};
+
+
+class Cluster
+{
+ Cluster(const Cluster&);
+ Cluster& operator=(const Cluster&);
+
+public:
+ Segment* const m_pSegment;
+ const size_t m_index;
+
+public:
+ static Cluster* Parse(Segment*, size_t, long long off);
+
+ Cluster(); //EndOfStream
+ ~Cluster();
+
+ bool EOS() const;
+
+ long long GetTimeCode(); //absolute, but not scaled
+ long long GetTime(); //absolute, and scaled (nanosecond units)
+
+ const BlockEntry* GetFirst();
+ const BlockEntry* GetLast();
+ const BlockEntry* GetNext(const BlockEntry*) const;
+ const BlockEntry* GetEntry(const Track*);
+protected:
+ Cluster(Segment*, size_t, long long off);
+
+private:
+ long long m_start;
+ long long m_size;
+ long long m_timecode;
+ BlockEntry** m_pEntries;
+ size_t m_entriesCount;
+
+ void Load();
+ void LoadBlockEntries();
+ void ParseBlockGroup(long long, long long, size_t);
+ void ParseSimpleBlock(long long, long long, size_t);
+
+};
+
+
+class Segment
+{
+ Segment(const Segment&);
+ Segment& operator=(const Segment&);
+
+private:
+ Segment(IMkvReader*, long long pos, long long size);
+
+public:
+ IMkvReader* const m_pReader;
+ const long long m_start; //posn of segment payload
+ const long long m_size; //size of segment payload
+ Cluster m_eos; //TODO: make private?
+
+ static long long CreateInstance(IMkvReader*, long long, Segment*&);
+ ~Segment();
+
+ //for big-bang loading (source filter)
+ long Load();
+
+ //for incremental loading (splitter)
+ long long Unparsed() const;
+ long long ParseHeaders();
+ long ParseCluster(Cluster*&, long long& newpos) const;
+ bool AddCluster(Cluster*, long long);
+
+ Tracks* GetTracks() const;
+ const SegmentInfo* const GetInfo() const;
+ long long GetDuration() const;
+
+ //NOTE: this turned out to be too inefficient.
+ //long long Load(long long time_nanoseconds);
+
+ Cluster* GetFirst();
+ Cluster* GetLast();
+ unsigned long GetCount() const;
+
+ Cluster* GetNext(const Cluster*);
+ Cluster* GetCluster(long long time_nanoseconds);
+
+private:
+ long long m_pos; //absolute file posn; what has been consumed so far
+ SegmentInfo* m_pInfo;
+ Tracks* m_pTracks;
+ Cluster** m_clusters;
+ size_t m_clusterCount;
+
+ void ParseSeekHead(long long pos, long long size, size_t*);
+ void ParseSeekEntry(long long pos, long long size, size_t*);
+ void ParseSecondarySeekHead(long long off, size_t*);
+};
+
+
+} //end namespace mkvparser
+
+#endif //MKVPARSER_HPP
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 33b0e81..74249f3 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -30,6 +30,7 @@
import android.content.res.Resources;
import android.database.Cursor;
import android.location.Address;
+import android.location.Criteria;
import android.location.GeocoderParams;
import android.location.IGpsStatusListener;
import android.location.IGpsStatusProvider;
@@ -68,6 +69,8 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -610,10 +613,10 @@
return out;
}
- public List<String> getProviders(boolean enabledOnly) {
+ public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
try {
synchronized (mLock) {
- return _getProvidersLocked(enabledOnly);
+ return _getProvidersLocked(criteria, enabledOnly);
}
} catch (SecurityException se) {
throw se;
@@ -623,7 +626,7 @@
}
}
- private List<String> _getProvidersLocked(boolean enabledOnly) {
+ private List<String> _getProvidersLocked(Criteria criteria, boolean enabledOnly) {
if (LOCAL_LOGV) {
Slog.v(TAG, "getProviders");
}
@@ -635,12 +638,225 @@
if (enabledOnly && !isAllowedBySettingsLocked(name)) {
continue;
}
+ if (criteria != null && !p.meetsCriteria(criteria)) {
+ continue;
+ }
out.add(name);
}
}
return out;
}
+ /**
+ * Returns the next looser power requirement, in the sequence:
+ *
+ * POWER_LOW -> POWER_MEDIUM -> POWER_HIGH -> NO_REQUIREMENT
+ */
+ private int nextPower(int power) {
+ switch (power) {
+ case Criteria.POWER_LOW:
+ return Criteria.POWER_MEDIUM;
+ case Criteria.POWER_MEDIUM:
+ return Criteria.POWER_HIGH;
+ case Criteria.POWER_HIGH:
+ return Criteria.NO_REQUIREMENT;
+ case Criteria.NO_REQUIREMENT:
+ default:
+ return Criteria.NO_REQUIREMENT;
+ }
+ }
+
+ /**
+ * Returns the next looser accuracy requirement, in the sequence:
+ *
+ * ACCURACY_FINE -> ACCURACY_APPROXIMATE-> NO_REQUIREMENT
+ */
+ private int nextAccuracy(int accuracy) {
+ if (accuracy == Criteria.ACCURACY_FINE) {
+ return Criteria.ACCURACY_COARSE;
+ } else {
+ return Criteria.NO_REQUIREMENT;
+ }
+ }
+
+ private class LpPowerComparator implements Comparator<LocationProviderInterface> {
+ public int compare(LocationProviderInterface l1, LocationProviderInterface l2) {
+ // Smaller is better
+ return (l1.getPowerRequirement() - l2.getPowerRequirement());
+ }
+
+ public boolean equals(LocationProviderInterface l1, LocationProviderInterface l2) {
+ return (l1.getPowerRequirement() == l2.getPowerRequirement());
+ }
+ }
+
+ private class LpAccuracyComparator implements Comparator<LocationProviderInterface> {
+ public int compare(LocationProviderInterface l1, LocationProviderInterface l2) {
+ // Smaller is better
+ return (l1.getAccuracy() - l2.getAccuracy());
+ }
+
+ public boolean equals(LocationProviderInterface l1, LocationProviderInterface l2) {
+ return (l1.getAccuracy() == l2.getAccuracy());
+ }
+ }
+
+ private class LpCapabilityComparator implements Comparator<LocationProviderInterface> {
+
+ private static final int ALTITUDE_SCORE = 4;
+ private static final int BEARING_SCORE = 4;
+ private static final int SPEED_SCORE = 4;
+
+ private int score(LocationProviderInterface p) {
+ return (p.supportsAltitude() ? ALTITUDE_SCORE : 0) +
+ (p.supportsBearing() ? BEARING_SCORE : 0) +
+ (p.supportsSpeed() ? SPEED_SCORE : 0);
+ }
+
+ public int compare(LocationProviderInterface l1, LocationProviderInterface l2) {
+ return (score(l2) - score(l1)); // Bigger is better
+ }
+
+ public boolean equals(LocationProviderInterface l1, LocationProviderInterface l2) {
+ return (score(l1) == score(l2));
+ }
+ }
+
+ private LocationProviderInterface best(List<String> providerNames) {
+ ArrayList<LocationProviderInterface> providers;
+ synchronized (mLock) {
+ providers = new ArrayList<LocationProviderInterface>(mProviders.size());
+ for (int i = mProviders.size() - 1; i >= 0; i--) {
+ providers.add(mProviders.get(i));
+ }
+ }
+
+ if (providers.size() < 2) {
+ return providers.get(0);
+ }
+
+ // First, sort by power requirement
+ Collections.sort(providers, new LpPowerComparator());
+ int power = providers.get(0).getPowerRequirement();
+ if (power < providers.get(1).getPowerRequirement()) {
+ return providers.get(0);
+ }
+
+ int idx, size;
+
+ ArrayList<LocationProviderInterface> tmp = new ArrayList<LocationProviderInterface>();
+ idx = 0;
+ size = providers.size();
+ while ((idx < size) && (providers.get(idx).getPowerRequirement() == power)) {
+ tmp.add(providers.get(idx));
+ idx++;
+ }
+
+ // Next, sort by accuracy
+ Collections.sort(tmp, new LpAccuracyComparator());
+ int acc = tmp.get(0).getAccuracy();
+ if (acc < tmp.get(1).getAccuracy()) {
+ return tmp.get(0);
+ }
+
+ ArrayList<LocationProviderInterface> tmp2 = new ArrayList<LocationProviderInterface>();
+ idx = 0;
+ size = tmp.size();
+ while ((idx < size) && (tmp.get(idx).getAccuracy() == acc)) {
+ tmp2.add(tmp.get(idx));
+ idx++;
+ }
+
+ // Finally, sort by capability "score"
+ Collections.sort(tmp2, new LpCapabilityComparator());
+ return tmp2.get(0);
+ }
+
+ /**
+ * Returns the name of the provider that best meets the given criteria. Only providers
+ * that are permitted to be accessed by the calling activity will be
+ * returned. If several providers meet the criteria, the one with the best
+ * accuracy is returned. If no provider meets the criteria,
+ * the criteria are loosened in the following sequence:
+ *
+ * <ul>
+ * <li> power requirement
+ * <li> accuracy
+ * <li> bearing
+ * <li> speed
+ * <li> altitude
+ * </ul>
+ *
+ * <p> Note that the requirement on monetary cost is not removed
+ * in this process.
+ *
+ * @param criteria the criteria that need to be matched
+ * @param enabledOnly if true then only a provider that is currently enabled is returned
+ * @return name of the provider that best matches the requirements
+ */
+ public String getBestProvider(Criteria criteria, boolean enabledOnly) {
+ List<String> goodProviders = getProviders(criteria, enabledOnly);
+ if (!goodProviders.isEmpty()) {
+ return best(goodProviders).getName();
+ }
+
+ // Make a copy of the criteria that we can modify
+ criteria = new Criteria(criteria);
+
+ // Loosen power requirement
+ int power = criteria.getPowerRequirement();
+ while (goodProviders.isEmpty() && (power != Criteria.NO_REQUIREMENT)) {
+ power = nextPower(power);
+ criteria.setPowerRequirement(power);
+ goodProviders = getProviders(criteria, enabledOnly);
+ }
+ if (!goodProviders.isEmpty()) {
+ return best(goodProviders).getName();
+ }
+
+ // Loosen accuracy requirement
+ int accuracy = criteria.getAccuracy();
+ while (goodProviders.isEmpty() && (accuracy != Criteria.NO_REQUIREMENT)) {
+ accuracy = nextAccuracy(accuracy);
+ criteria.setAccuracy(accuracy);
+ goodProviders = getProviders(criteria, enabledOnly);
+ }
+ if (!goodProviders.isEmpty()) {
+ return best(goodProviders).getName();
+ }
+
+ // Remove bearing requirement
+ criteria.setBearingRequired(false);
+ goodProviders = getProviders(criteria, enabledOnly);
+ if (!goodProviders.isEmpty()) {
+ return best(goodProviders).getName();
+ }
+
+ // Remove speed requirement
+ criteria.setSpeedRequired(false);
+ goodProviders = getProviders(criteria, enabledOnly);
+ if (!goodProviders.isEmpty()) {
+ return best(goodProviders).getName();
+ }
+
+ // Remove altitude requirement
+ criteria.setAltitudeRequired(false);
+ goodProviders = getProviders(criteria, enabledOnly);
+ if (!goodProviders.isEmpty()) {
+ return best(goodProviders).getName();
+ }
+
+ return null;
+ }
+
+ public boolean providerMeetsCriteria(String provider, Criteria criteria) {
+ LocationProviderInterface p = mProvidersByName.get(provider);
+ if (p == null) {
+ throw new IllegalArgumentException("provider=" + provider);
+ }
+ return p.meetsCriteria(criteria);
+ }
+
private void updateProvidersLocked() {
for (int i = mProviders.size() - 1; i >= 0; i--) {
LocationProviderInterface p = mProviders.get(i);
@@ -717,6 +933,7 @@
final Receiver mReceiver;
final long mMinTime;
final float mMinDistance;
+ final boolean mSingleShot;
final int mUid;
Location mLastFixBroadcast;
long mLastStatusBroadcast;
@@ -724,12 +941,13 @@
/**
* Note: must be constructed with lock held.
*/
- UpdateRecord(String provider, long minTime, float minDistance,
+ UpdateRecord(String provider, long minTime, float minDistance, boolean singleShot,
Receiver receiver, int uid) {
mProvider = provider;
mReceiver = receiver;
mMinTime = minTime;
mMinDistance = minDistance;
+ mSingleShot = singleShot;
mUid = uid;
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
@@ -764,6 +982,7 @@
pw.println(prefix + this);
pw.println(prefix + "mProvider=" + mProvider + " mReceiver=" + mReceiver);
pw.println(prefix + "mMinTime=" + mMinTime + " mMinDistance=" + mMinDistance);
+ pw.println(prefix + "mSingleShot=" + mSingleShot);
pw.println(prefix + "mUid=" + mUid);
pw.println(prefix + "mLastFixBroadcast:");
if (mLastFixBroadcast != null) {
@@ -819,12 +1038,21 @@
return false;
}
- public void requestLocationUpdates(String provider,
- long minTime, float minDistance, ILocationListener listener) {
-
+ public void requestLocationUpdates(String provider, Criteria criteria,
+ long minTime, float minDistance, boolean singleShot, ILocationListener listener) {
+ if (criteria != null) {
+ // FIXME - should we consider using multiple providers simultaneously
+ // rather than only the best one?
+ // Should we do anything different for single shot fixes?
+ provider = getBestProvider(criteria, true);
+ if (provider == null) {
+ throw new IllegalArgumentException("no providers found for criteria");
+ }
+ }
try {
synchronized (mLock) {
- requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(listener));
+ requestLocationUpdatesLocked(provider, minTime, minDistance, singleShot,
+ getReceiver(listener));
}
} catch (SecurityException se) {
throw se;
@@ -835,11 +1063,21 @@
}
}
- public void requestLocationUpdatesPI(String provider,
- long minTime, float minDistance, PendingIntent intent) {
+ public void requestLocationUpdatesPI(String provider, Criteria criteria,
+ long minTime, float minDistance, boolean singleShot, PendingIntent intent) {
+ if (criteria != null) {
+ // FIXME - should we consider using multiple providers simultaneously
+ // rather than only the best one?
+ // Should we do anything different for single shot fixes?
+ provider = getBestProvider(criteria, true);
+ if (provider == null) {
+ throw new IllegalArgumentException("no providers found for criteria");
+ }
+ }
try {
synchronized (mLock) {
- requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(intent));
+ requestLocationUpdatesLocked(provider, minTime, minDistance, singleShot,
+ getReceiver(intent));
}
} catch (SecurityException se) {
throw se;
@@ -850,8 +1088,8 @@
}
}
- private void requestLocationUpdatesLocked(String provider,
- long minTime, float minDistance, Receiver receiver) {
+ private void requestLocationUpdatesLocked(String provider, long minTime, float minDistance,
+ boolean singleShot, Receiver receiver) {
if (LOCAL_LOGV) {
Slog.v(TAG, "_requestLocationUpdates: listener = " + receiver);
}
@@ -868,7 +1106,8 @@
boolean newUid = !providerHasListener(provider, callingUid, null);
long identity = Binder.clearCallingIdentity();
try {
- UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, receiver, callingUid);
+ UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, singleShot,
+ receiver, callingUid);
UpdateRecord oldRecord = receiver.mUpdateRecords.put(provider, r);
if (oldRecord != null) {
oldRecord.disposeLocked();
@@ -882,7 +1121,11 @@
if (isProviderEnabled) {
long minTimeForProvider = getMinTimeLocked(provider);
p.setMinTime(minTimeForProvider);
- p.enableLocationTracking(true);
+ // try requesting single shot if singleShot is true, and fall back to
+ // regular location tracking if requestSingleShotFix() is not supported
+ if (!singleShot || !p.requestSingleShotFix()) {
+ p.enableLocationTracking(true);
+ }
} else {
// Notify the listener that updates are currently disabled
receiver.callProviderEnabledLocked(provider, false);
@@ -1288,7 +1531,8 @@
for (int i = mProviders.size() - 1; i >= 0; i--) {
LocationProviderInterface provider = mProviders.get(i);
- requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityReceiver);
+ requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f,
+ false, mProximityReceiver);
}
}
}
@@ -1486,6 +1730,7 @@
for (int i=0; i<N; i++) {
UpdateRecord r = records.get(i);
Receiver receiver = r.mReceiver;
+ boolean receiverDead = false;
Location lastLoc = r.mLastFixBroadcast;
if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) {
@@ -1497,10 +1742,7 @@
}
if (!receiver.callLocationChangedLocked(location)) {
Slog.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
- if (deadReceivers == null) {
- deadReceivers = new ArrayList<Receiver>();
- }
- deadReceivers.add(receiver);
+ receiverDead = true;
}
}
@@ -1510,13 +1752,18 @@
r.mLastStatusBroadcast = newStatusUpdateTime;
if (!receiver.callStatusChangedLocked(provider, status, extras)) {
+ receiverDead = true;
Slog.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
- if (deadReceivers == null) {
- deadReceivers = new ArrayList<Receiver>();
- }
- if (!deadReceivers.contains(receiver)) {
- deadReceivers.add(receiver);
- }
+ }
+ }
+
+ // remove receiver if it is dead or we just processed a single shot request
+ if (receiverDead || r.mSingleShot) {
+ if (deadReceivers == null) {
+ deadReceivers = new ArrayList<Receiver>();
+ }
+ if (!deadReceivers.contains(receiver)) {
+ deadReceivers.add(receiver);
}
}
}
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index 19c9018..0cf67a3 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -150,6 +150,7 @@
private static final int UPDATE_LOCATION = 7;
private static final int ADD_LISTENER = 8;
private static final int REMOVE_LISTENER = 9;
+ private static final int REQUEST_SINGLE_SHOT = 10;
private static final String PROPERTIES_FILE = "/etc/gps.conf";
@@ -190,6 +191,9 @@
// true if we started navigation
private boolean mStarted;
+ // true if single shot request is in progress
+ private boolean mSingleShot;
+
// capabilities of the GPS engine
private int mEngineCapabilities;
@@ -318,7 +322,7 @@
if (action.equals(ALARM_WAKEUP)) {
if (DEBUG) Log.d(TAG, "ALARM_WAKEUP");
- startNavigating();
+ startNavigating(false);
} else if (action.equals(ALARM_TIMEOUT)) {
if (DEBUG) Log.d(TAG, "ALARM_TIMEOUT");
hibernate();
@@ -599,6 +603,14 @@
}
/**
+ * Returns true if this provider meets the given criteria,
+ * false otherwise.
+ */
+ public boolean meetsCriteria(Criteria criteria) {
+ return (criteria.getPowerRequirement() != Criteria.POWER_LOW);
+ }
+
+ /**
* Returns the horizontal accuracy of this provider
*
* @return the accuracy of location from this provider, as one
@@ -707,6 +719,7 @@
}
public void enableLocationTracking(boolean enable) {
+ // FIXME - should set a flag here to avoid race conditions with single shot request
synchronized (mHandler) {
sendMessage(ENABLE_TRACKING, (enable ? 1 : 0), null);
}
@@ -716,7 +729,7 @@
if (enable) {
mTTFF = 0;
mLastFixTime = 0;
- startNavigating();
+ startNavigating(false);
} else {
if (!hasCapability(GPS_CAPABILITY_SCHEDULING)) {
mAlarmManager.cancel(mWakeupIntent);
@@ -726,6 +739,25 @@
}
}
+ public boolean requestSingleShotFix() {
+ if (mStarted) {
+ // cannot do single shot if already navigating
+ return false;
+ }
+ synchronized (mHandler) {
+ mHandler.removeMessages(REQUEST_SINGLE_SHOT);
+ Message m = Message.obtain(mHandler, REQUEST_SINGLE_SHOT);
+ mHandler.sendMessage(m);
+ }
+ return true;
+ }
+
+ private void handleRequestSingleShot() {
+ mTTFF = 0;
+ mLastFixTime = 0;
+ startNavigating(true);
+ }
+
public void setMinTime(long minTime) {
if (DEBUG) Log.d(TAG, "setMinTime " + minTime);
@@ -875,16 +907,20 @@
return false;
}
- private void startNavigating() {
+ private void startNavigating(boolean singleShot) {
if (!mStarted) {
if (DEBUG) Log.d(TAG, "startNavigating");
mStarted = true;
- if (hasCapability(GPS_CAPABILITY_MSB) &&
- Settings.Secure.getInt(mContext.getContentResolver(),
+ mSingleShot = singleShot;
+ mPositionMode = GPS_POSITION_MODE_STANDALONE;
+
+ if (Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ASSISTED_GPS_ENABLED, 1) != 0) {
- mPositionMode = GPS_POSITION_MODE_MS_BASED;
- } else {
- mPositionMode = GPS_POSITION_MODE_STANDALONE;
+ if (singleShot && hasCapability(GPS_CAPABILITY_MSA)) {
+ mPositionMode = GPS_POSITION_MODE_MS_ASSISTED;
+ } else if (hasCapability(GPS_CAPABILITY_MSA)) {
+ mPositionMode = GPS_POSITION_MODE_MS_BASED;
+ }
}
int interval = (hasCapability(GPS_CAPABILITY_SCHEDULING) ? mFixInterval : 1000);
@@ -918,6 +954,7 @@
if (DEBUG) Log.d(TAG, "stopNavigating");
if (mStarted) {
mStarted = false;
+ mSingleShot = false;
native_stop();
mTTFF = 0;
mLastFixTime = 0;
@@ -1008,6 +1045,9 @@
}
}
+ if (mSingleShot) {
+ stopNavigating();
+ }
if (mStarted && mStatus != LocationProvider.AVAILABLE) {
// we want to time out if we do not receive a fix
// within the time out and we are requesting infrequent fixes
@@ -1022,7 +1062,7 @@
updateStatus(LocationProvider.AVAILABLE, mSvCount);
}
- if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mFixInterval > 1000) {
+ if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mStarted && mFixInterval > 1000) {
if (DEBUG) Log.d(TAG, "got fix, hibernating");
hibernate();
}
@@ -1133,7 +1173,8 @@
}
}
- updateStatus(mStatus, svCount);
+ // return number of sets used in fix instead of total
+ updateStatus(mStatus, Integer.bitCount(mSvMasks[USED_FOR_FIX_MASK]));
if (mNavigating && mStatus == LocationProvider.AVAILABLE && mLastFixTime > 0 &&
System.currentTimeMillis() - mLastFixTime > RECENT_FIX_TIMEOUT) {
@@ -1369,6 +1410,9 @@
case ENABLE_TRACKING:
handleEnableLocationTracking(msg.arg1 == 1);
break;
+ case REQUEST_SINGLE_SHOT:
+ handleRequestSingleShot();
+ break;
case UPDATE_NETWORK_STATE:
handleUpdateNetworkState(msg.arg1, (NetworkInfo)msg.obj);
break;
diff --git a/services/java/com/android/server/location/LocationProviderInterface.java b/services/java/com/android/server/location/LocationProviderInterface.java
index a472143..084ab81 100644
--- a/services/java/com/android/server/location/LocationProviderInterface.java
+++ b/services/java/com/android/server/location/LocationProviderInterface.java
@@ -16,6 +16,7 @@
package com.android.server.location;
+import android.location.Criteria;
import android.location.Location;
import android.net.NetworkInfo;
import android.os.Bundle;
@@ -35,6 +36,7 @@
boolean supportsSpeed();
boolean supportsBearing();
int getPowerRequirement();
+ boolean meetsCriteria(Criteria criteria);
int getAccuracy();
boolean isEnabled();
void enable();
@@ -42,6 +44,8 @@
int getStatus(Bundle extras);
long getStatusUpdateTime();
void enableLocationTracking(boolean enable);
+ /* returns false if single shot is not supported */
+ boolean requestSingleShotFix();
String getInternalState();
void setMinTime(long minTime);
void updateNetworkState(int state, NetworkInfo info);
diff --git a/services/java/com/android/server/location/LocationProviderProxy.java b/services/java/com/android/server/location/LocationProviderProxy.java
index 3e118f9..24d7737 100644
--- a/services/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/java/com/android/server/location/LocationProviderProxy.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.location.Criteria;
import android.location.ILocationProvider;
import android.location.Location;
import android.net.NetworkInfo;
@@ -97,7 +98,7 @@
if (mCachedAttributes == null) {
try {
- mCachedAttributes = new DummyLocationProvider(mName);
+ mCachedAttributes = new DummyLocationProvider(mName, null);
mCachedAttributes.setRequiresNetwork(provider.requiresNetwork());
mCachedAttributes.setRequiresSatellite(provider.requiresSatellite());
mCachedAttributes.setRequiresCell(provider.requiresCell());
@@ -199,6 +200,39 @@
}
}
+ public boolean meetsCriteria(Criteria criteria) {
+ ILocationProvider provider;
+ synchronized (mServiceConnection) {
+ provider = mProvider;
+ }
+ if (provider != null) {
+ try {
+ return provider.meetsCriteria(criteria);
+ } catch (RemoteException e) {
+ }
+ }
+ // default implementation if we lost connection to the provider
+ if ((criteria.getAccuracy() != Criteria.NO_REQUIREMENT) &&
+ (criteria.getAccuracy() < getAccuracy())) {
+ return false;
+ }
+ int criteriaPower = criteria.getPowerRequirement();
+ if ((criteriaPower != Criteria.NO_REQUIREMENT) &&
+ (criteriaPower < getPowerRequirement())) {
+ return false;
+ }
+ if (criteria.isAltitudeRequired() && !supportsAltitude()) {
+ return false;
+ }
+ if (criteria.isSpeedRequired() && !supportsSpeed()) {
+ return false;
+ }
+ if (criteria.isBearingRequired() && !supportsBearing()) {
+ return false;
+ }
+ return true;
+ }
+
public int getAccuracy() {
if (mCachedAttributes != null) {
return mCachedAttributes.getAccuracy();
@@ -297,6 +331,10 @@
}
}
+ public boolean requestSingleShotFix() {
+ return false;
+ }
+
public long getMinTime() {
return mMinTime;
}
diff --git a/services/java/com/android/server/location/MockProvider.java b/services/java/com/android/server/location/MockProvider.java
index e3f33469..01b34b7 100644
--- a/services/java/com/android/server/location/MockProvider.java
+++ b/services/java/com/android/server/location/MockProvider.java
@@ -16,6 +16,7 @@
package com.android.server.location;
+import android.location.Criteria;
import android.location.ILocationManager;
import android.location.Location;
import android.location.LocationProvider;
@@ -138,6 +139,28 @@
return mSupportsSpeed;
}
+ public boolean meetsCriteria(Criteria criteria) {
+ if ((criteria.getAccuracy() != Criteria.NO_REQUIREMENT) &&
+ (criteria.getAccuracy() < mAccuracy)) {
+ return false;
+ }
+ int criteriaPower = criteria.getPowerRequirement();
+ if ((criteriaPower != Criteria.NO_REQUIREMENT) &&
+ (criteriaPower < mPowerRequirement)) {
+ return false;
+ }
+ if (criteria.isAltitudeRequired() && !mSupportsAltitude) {
+ return false;
+ }
+ if (criteria.isSpeedRequired() && !mSupportsSpeed) {
+ return false;
+ }
+ if (criteria.isBearingRequired() && !mSupportsBearing) {
+ return false;
+ }
+ return true;
+ }
+
public void setLocation(Location l) {
mLocation.set(l);
mHasLocation = true;
@@ -174,6 +197,10 @@
public void enableLocationTracking(boolean enable) {
}
+ public boolean requestSingleShotFix() {
+ return false;
+ }
+
public void setMinTime(long minTime) {
}
diff --git a/services/java/com/android/server/location/PassiveProvider.java b/services/java/com/android/server/location/PassiveProvider.java
index 5ed1558..7fc93f8 100644
--- a/services/java/com/android/server/location/PassiveProvider.java
+++ b/services/java/com/android/server/location/PassiveProvider.java
@@ -16,6 +16,7 @@
package com.android.server.location;
+import android.location.Criteria;
import android.location.ILocationManager;
import android.location.Location;
import android.location.LocationManager;
@@ -79,6 +80,11 @@
return -1;
}
+ public boolean meetsCriteria(Criteria criteria) {
+ // We do not want to match the special passive provider based on criteria.
+ return false;
+ }
+
public int getAccuracy() {
return -1;
}
@@ -113,6 +119,10 @@
mTracking = enable;
}
+ public boolean requestSingleShotFix() {
+ return false;
+ }
+
public void setMinTime(long minTime) {
}