Merge "Do not release the wifi lock if the screen is off." into gingerbread
diff --git a/api/current.xml b/api/current.xml
index a2bce1d..2a2200d 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -24549,7 +24549,7 @@
</parameter>
</method>
<method name="remove"
- return="void"
+ return="int"
abstract="false"
native="false"
synchronized="false"
@@ -24558,7 +24558,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="id" type="long">
+<parameter name="ids" type="long...">
</parameter>
</method>
<field name="ACTION_DOWNLOAD_COMPLETE"
@@ -24951,7 +24951,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="id" type="long">
+<parameter name="ids" type="long...">
</parameter>
</method>
<method name="setFilterByStatus"
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index e66f52a..c08f1fc 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2739,14 +2739,15 @@
private final File mBackupFile;
private final int mMode;
- private Map<String, Object> mMap; // guarded by 'this'
- private long mTimestamp; // guarded by 'this'
+ private Map<String, Object> mMap; // guarded by 'this'
private int mDiskWritesInFlight = 0; // guarded by 'this'
- private boolean mLoaded = false; // guarded by 'this'
+ private boolean mLoaded = false; // guarded by 'this'
+ private long mStatTimestamp; // guarded by 'this'
+ private long mStatSize; // guarded by 'this'
private final Object mWritingToDiskLock = new Object();
private static final Object mContent = new Object();
- private WeakHashMap<OnSharedPreferenceChangeListener, Object> mListeners;
+ private final WeakHashMap<OnSharedPreferenceChangeListener, Object> mListeners;
SharedPreferencesImpl(
File file, int mode, Map initialContents) {
@@ -2757,7 +2758,7 @@
mMap = initialContents != null ? initialContents : new HashMap<String, Object>();
FileStatus stat = new FileStatus();
if (FileUtils.getFileStatus(file.getPath(), stat)) {
- mTimestamp = stat.mtime;
+ mStatTimestamp = stat.mtime;
}
mListeners = new WeakHashMap<OnSharedPreferenceChangeListener, Object>();
}
@@ -2784,7 +2785,7 @@
return true;
}
synchronized (this) {
- return mTimestamp != stat.mtime;
+ return mStatTimestamp != stat.mtime || mStatSize != stat.size;
}
}
@@ -3165,7 +3166,8 @@
FileStatus stat = new FileStatus();
if (FileUtils.getFileStatus(mFile.getPath(), stat)) {
synchronized (this) {
- mTimestamp = stat.mtime;
+ mStatTimestamp = stat.mtime;
+ mStatSize = stat.size;
}
}
// Writing was successful, delete the backup file if there is one.
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index c476b8f..6256303 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -567,18 +567,18 @@
*/
public static final int ORDER_DESCENDING = 2;
- private Long mId = null;
+ private long[] mIds = null;
private Integer mStatusFlags = null;
private String mOrderByColumn = Downloads.COLUMN_LAST_MODIFICATION;
private int mOrderDirection = ORDER_DESCENDING;
private boolean mOnlyIncludeVisibleInDownloadsUi = false;
/**
- * Include only the download with the given ID.
+ * Include only the downloads with the given IDs.
* @return this object
*/
- public Query setFilterById(long id) {
- mId = id;
+ public Query setFilterById(long... ids) {
+ mIds = ids;
return this;
}
@@ -639,9 +639,11 @@
Cursor runQuery(ContentResolver resolver, String[] projection, Uri baseUri) {
Uri uri = baseUri;
List<String> selectionParts = new ArrayList<String>();
+ String[] selectionArgs = null;
- if (mId != null) {
- uri = ContentUris.withAppendedId(uri, mId);
+ if (mIds != null) {
+ selectionParts.add(getWhereClauseForIds(mIds));
+ selectionArgs = getWhereArgsForIds(mIds);
}
if (mStatusFlags != null) {
@@ -676,7 +678,7 @@
String orderDirection = (mOrderDirection == ORDER_ASCENDING ? "ASC" : "DESC");
String orderBy = mOrderByColumn + " " + orderDirection;
- return resolver.query(uri, projection, selection, null, orderBy);
+ return resolver.query(uri, projection, selection, selectionArgs, orderBy);
}
private String joinStrings(String joiner, Iterable<String> parts) {
@@ -738,17 +740,28 @@
}
/**
- * Cancel a download and remove it from the download manager. The download will be stopped if
+ * Cancel downloads and remove them from the download manager. Each download will be stopped if
* it was running, and it will no longer be accessible through the download manager. If a file
- * was already downloaded, it will not be deleted.
+ * was already downloaded to external storage, it will not be deleted.
*
- * @param id the ID of the download
+ * @param ids the IDs of the downloads to remove
+ * @return the number of downloads actually removed
*/
- public void remove(long id) {
- int numDeleted = mResolver.delete(getDownloadUri(id), null, null);
- if (numDeleted == 0) {
- throw new IllegalArgumentException("Download " + id + " does not exist");
+ public int remove(long... ids) {
+ StringBuilder whereClause = new StringBuilder();
+ String[] whereArgs = new String[ids.length];
+
+ whereClause.append(Downloads.Impl._ID + " IN (");
+ for (int i = 0; i < ids.length; i++) {
+ if (i > 0) {
+ whereClause.append(",");
+ }
+ whereClause.append("?");
+ whereArgs[i] = Long.toString(ids[i]);
}
+ whereClause.append(")");
+
+ return mResolver.delete(mBaseUri, whereClause.toString(), whereArgs);
}
/**
@@ -776,20 +789,20 @@
}
/**
- * Restart the given download, which must have already completed (successfully or not). This
+ * Restart the given downloads, which must have already completed (successfully or not). This
* method will only work when called from within the download manager's process.
- * @param id the ID of the download
+ * @param ids the IDs of the downloads
* @hide
*/
- public void restartDownload(long id) {
- Cursor cursor = query(new Query().setFilterById(id));
+ public void restartDownload(long... ids) {
+ Cursor cursor = query(new Query().setFilterById(ids));
try {
- if (!cursor.moveToFirst()) {
- throw new IllegalArgumentException("No download with id " + id);
- }
- int status = cursor.getInt(cursor.getColumnIndex(COLUMN_STATUS));
- if (status != STATUS_SUCCESSFUL && status != STATUS_FAILED) {
- throw new IllegalArgumentException("Cannot restart incomplete download: " + id);
+ for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
+ int status = cursor.getInt(cursor.getColumnIndex(COLUMN_STATUS));
+ if (status != STATUS_SUCCESSFUL && status != STATUS_FAILED) {
+ throw new IllegalArgumentException("Cannot restart incomplete download: "
+ + cursor.getLong(cursor.getColumnIndex(COLUMN_ID)));
+ }
}
} finally {
cursor.close();
@@ -800,7 +813,7 @@
values.put(Downloads.Impl.COLUMN_TOTAL_BYTES, -1);
values.putNull(Downloads.Impl._DATA);
values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_PENDING);
- mResolver.update(getDownloadUri(id), values, null, null);
+ mResolver.update(mBaseUri, values, getWhereClauseForIds(ids), getWhereArgsForIds(ids));
}
/**
@@ -811,6 +824,33 @@
}
/**
+ * Get a parameterized SQL WHERE clause to select a bunch of IDs.
+ */
+ static String getWhereClauseForIds(long[] ids) {
+ StringBuilder whereClause = new StringBuilder();
+ whereClause.append(Downloads.Impl._ID + " IN (");
+ for (int i = 0; i < ids.length; i++) {
+ if (i > 0) {
+ whereClause.append(",");
+ }
+ whereClause.append("?");
+ }
+ whereClause.append(")");
+ return whereClause.toString();
+ }
+
+ /**
+ * Get the selection args for a clause returned by {@link #getWhereClauseForIds(long[])}.
+ */
+ static String[] getWhereArgsForIds(long[] ids) {
+ String[] whereArgs = new String[ids.length];
+ for (int i = 0; i < ids.length; i++) {
+ whereArgs[i] = Long.toString(ids[i]);
+ }
+ return whereArgs;
+ }
+
+ /**
* This class wraps a cursor returned by DownloadProvider -- the "underlying cursor" -- and
* presents a different set of columns, those defined in the DownloadManager.COLUMN_* constants.
* Some columns correspond directly to underlying values while others are computed from
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index d49c8be..d67e6f5 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -449,6 +449,10 @@
public static final int STATE_WAKE_LOCK_FLAG = 1<<17;
public static final int STATE_SENSOR_ON_FLAG = 1<<16;
+ public static final int MOST_INTERESTING_STATES =
+ STATE_BATTERY_PLUGGED_FLAG | STATE_SCREEN_ON_FLAG
+ | STATE_GPS_ON_FLAG | STATE_PHONE_IN_CALL_FLAG;
+
public int states;
public HistoryItem() {
diff --git a/core/java/android/widget/EdgeGlow.java b/core/java/android/widget/EdgeGlow.java
index 7a990ad..8d14462 100644
--- a/core/java/android/widget/EdgeGlow.java
+++ b/core/java/android/widget/EdgeGlow.java
@@ -145,10 +145,9 @@
mEdgeScaleY = mEdgeScaleYStart = Math.max(
HELD_EDGE_SCALE_Y, Math.min(distance * PULL_DISTANCE_EDGE_FACTOR, 1.f));
- mGlowAlpha = mGlowAlphaStart = Math.min(
+ mGlowAlpha = mGlowAlphaStart = Math.min(MAX_ALPHA,
mGlowAlpha +
- (Math.abs(deltaDistance) * PULL_DISTANCE_ALPHA_GLOW_FACTOR),
- MAX_ALPHA);
+ (Math.abs(deltaDistance) * PULL_DISTANCE_ALPHA_GLOW_FACTOR));
float glowChange = Math.abs(deltaDistance);
if (deltaDistance > 0 && mPullDistance < 0) {
@@ -157,8 +156,10 @@
if (mPullDistance == 0) {
mGlowScaleY = 0;
}
- mGlowScaleY = mGlowScaleYStart = Math.max(
- 0, mGlowScaleY + glowChange * PULL_DISTANCE_GLOW_FACTOR);
+
+ // Do not allow glow to get larger than MAX_GLOW_HEIGHT.
+ mGlowScaleY = mGlowScaleYStart = Math.min(MAX_GLOW_HEIGHT, Math.max(
+ 0, mGlowScaleY + glowChange * PULL_DISTANCE_GLOW_FACTOR));
mEdgeAlphaFinish = mEdgeAlpha;
mEdgeScaleYFinish = mEdgeScaleY;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index d1974dc..138aefa 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6549,11 +6549,11 @@
int selEnd = getSelectionEnd();
if (!mFrozenWithFocus || (selStart < 0 || selEnd < 0)) {
+ // If a tap was used to give focus to that view, move cursor at tap position.
// Has to be done before onTakeFocus, which can be overloaded.
- if (mLastTouchOffset >= 0) {
- // Can happen when a TextView is displayed after its content has been deleted.
- mLastTouchOffset = Math.min(mLastTouchOffset, mText.length());
- Selection.setSelection((Spannable) mText, mLastTouchOffset);
+ final int lastTapPosition = getLastTapPosition();
+ if (lastTapPosition >= 0) {
+ Selection.setSelection((Spannable) mText, lastTapPosition);
}
if (mMovement != null) {
@@ -6614,7 +6614,9 @@
terminateTextSelectionMode();
}
- mLastTouchOffset = -1;
+ if (mSelectionModifierCursorController != null) {
+ ((SelectionModifierCursorController) mSelectionModifierCursorController).resetTouchOffsets();
+ }
}
startStopMarquee(focused);
@@ -6626,6 +6628,24 @@
super.onFocusChanged(focused, direction, previouslyFocusedRect);
}
+ private int getLastTapPosition() {
+ if (mSelectionModifierCursorController != null) {
+ int lastTapPosition = ((SelectionModifierCursorController)
+ mSelectionModifierCursorController).getMinTouchOffset();
+ if (lastTapPosition >= 0) {
+ // Safety check, should not be possible.
+ if (lastTapPosition > mText.length()) {
+ Log.e(LOG_TAG, "Invalid tap focus position (" + lastTapPosition + " vs "
+ + mText.length() + ")");
+ lastTapPosition = mText.length();
+ }
+ return lastTapPosition;
+ }
+ }
+
+ return -1;
+ }
+
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
@@ -6717,7 +6737,7 @@
// Tapping outside stops selection mode, if any
stopTextSelectionMode();
- if (mInsertionPointCursorController != null) {
+ if (mInsertionPointCursorController != null && mText.length() > 0) {
mInsertionPointCursorController.show();
}
}
@@ -7255,7 +7275,7 @@
int minOffset, maxOffset;
- if (mDPadCenterIsDown || mEnterKeyIsDown) {
+ if (mContextMenuTriggeredByKey) {
minOffset = getSelectionStart();
maxOffset = getSelectionEnd();
} else {
@@ -7286,7 +7306,8 @@
}
private String getWordForDictionary() {
- long wordLimits = getWordLimitsAt(getSelectionStart());
+ int seedPosition = mContextMenuTriggeredByKey ? getSelectionStart() : getLastTapPosition();
+ long wordLimits = getWordLimitsAt(seedPosition);
if (wordLimits >= 0) {
int start = extractRangeStartFromLong(wordLimits);
int end = extractRangeEndFromLong(wordLimits);
@@ -7336,6 +7357,14 @@
protected void onCreateContextMenu(ContextMenu menu) {
super.onCreateContextMenu(menu);
boolean added = false;
+ mContextMenuTriggeredByKey = mDPadCenterIsDown || mEnterKeyIsDown;
+ // Problem with context menu on long press: the menu appears while the key in down and when
+ // the key is released, the view does not receive the key_up event. This ensures that the
+ // state is reset whenever the context menu action is displayed.
+ // mContextMenuTriggeredByKey saved that state so that it is available in
+ // onTextContextMenuItem. We cannot simply clear these flags in onTextContextMenuItem since
+ // it may not be called (if the user/ discards the context menu with the back key).
+ mDPadCenterIsDown = mEnterKeyIsDown = false;
if (mIsInTextSelectionMode) {
MenuHandler handler = new MenuHandler();
@@ -7361,21 +7390,6 @@
added = true;
}
} else {
- /*
- if (!isFocused()) {
- if (isFocusable() && mInput != null) {
- if (canCopy()) {
- MenuHandler handler = new MenuHandler();
- menu.add(0, ID_COPY, 0, com.android.internal.R.string.copy).
- setOnMenuItemClickListener(handler).
- setAlphabeticShortcut('c');
- menu.setHeaderTitle(com.android.internal.R.string.editTextMenuTitle);
- }
- }
-
- //return;
- }
- */
MenuHandler handler = new MenuHandler();
if (canSelectText()) {
@@ -7529,7 +7543,6 @@
case ID_ADD_TO_DICTIONARY:
String word = getWordForDictionary();
-
if (word != null) {
Intent i = new Intent("com.android.settings.USER_DICTIONARY_INSERT");
i.putExtra("word", word);
@@ -8013,6 +8026,7 @@
SelectionModifierCursorController() {
mStartHandle = new HandleView(this, HandleView.LEFT);
mEndHandle = new HandleView(this, HandleView.RIGHT);
+ resetTouchOffsets();
}
public void show() {
@@ -8095,14 +8109,16 @@
}
public boolean onTouchEvent(MotionEvent event) {
- if (isFocused() && isTextEditable()) {
+ // This is done even when the View does not have focus, so that long presses can start
+ // selection and tap can move cursor from this tap position.
+ if (isTextEditable()) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
final int x = (int) event.getX();
final int y = (int) event.getY();
// Remember finger down position, to be able to start selection from there
- mMinTouchOffset = mMaxTouchOffset = mLastTouchOffset = getOffset(x, y);
+ mMinTouchOffset = mMaxTouchOffset = getOffset(x, y);
break;
@@ -8142,6 +8158,10 @@
return mMaxTouchOffset;
}
+ public void resetTouchOffsets() {
+ mMinTouchOffset = mMaxTouchOffset = -1;
+ }
+
/**
* @return true iff this controller is currently used to move the selection start.
*/
@@ -8259,11 +8279,11 @@
private CursorController mInsertionPointCursorController;
private CursorController mSelectionModifierCursorController;
private boolean mIsInTextSelectionMode = false;
- private int mLastTouchOffset = -1;
// These are needed to desambiguate a long click. If the long click comes from ones of these, we
// select from the current cursor position. Otherwise, select from long pressed position.
private boolean mDPadCenterIsDown = false;
private boolean mEnterKeyIsDown = false;
+ private boolean mContextMenuTriggeredByKey = false;
// Created once and shared by different CursorController helper methods.
// Only one cursor controller is active at any time which prevent race conditions.
private static Rect sCursorControllerTempRect = new Rect();
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index b033fad..a9e5052 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -72,6 +72,9 @@
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
+ // No, really, THIS is the maximum number of items we will record in the history.
+ private static final int MAX_MAX_HISTORY_ITEMS = 3000;
+
// The maximum number of names wakelocks we will keep track of
// per uid; once the limit is reached, we batch the remaining wakelocks
// in to one common name.
@@ -1169,15 +1172,20 @@
mBtHeadset = headset;
}
+ int mChangedStates = 0;
+
void addHistoryRecordLocked(long curTime) {
if (!mHaveBatteryLevel || !mRecordingHistory) {
return;
}
// If the current time is basically the same as the last time,
- // just collapse into one record.
+ // and no states have since the last recorded entry changed and
+ // are now resetting back to their original value, then just collapse
+ // into one record.
if (mHistoryEnd != null && mHistoryEnd.cmd == HistoryItem.CMD_UPDATE
- && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+500)) {
+ && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+2000)
+ && ((mHistoryEnd.states^mHistoryCur.states)&mChangedStates) == 0) {
// If the current is the same as the one before, then we no
// longer need the entry.
if (mHistoryLastEnd != null && mHistoryLastEnd.cmd == HistoryItem.CMD_UPDATE
@@ -1188,20 +1196,29 @@
mHistoryEnd = mHistoryLastEnd;
mHistoryLastEnd = null;
} else {
+ mChangedStates |= mHistoryEnd.states^mHistoryCur.states;
mHistoryEnd.setTo(mHistoryEnd.time, HistoryItem.CMD_UPDATE, mHistoryCur);
}
return;
}
- if (mNumHistoryItems == MAX_HISTORY_ITEMS) {
+ mChangedStates = 0;
+
+ if (mNumHistoryItems == MAX_HISTORY_ITEMS
+ || mNumHistoryItems == MAX_MAX_HISTORY_ITEMS) {
addHistoryRecordLocked(curTime, HistoryItem.CMD_OVERFLOW);
}
if (mNumHistoryItems >= MAX_HISTORY_ITEMS) {
// Once we've reached the maximum number of items, we only
+ // record changes to the battery level and the most interesting states.
+ // Once we've reached the maximum maximum number of items, we only
// record changes to the battery level.
if (mHistoryEnd != null && mHistoryEnd.batteryLevel
- == mHistoryCur.batteryLevel) {
+ == mHistoryCur.batteryLevel &&
+ (mNumHistoryItems >= MAX_MAX_HISTORY_ITEMS
+ || ((mHistoryEnd.states^mHistoryCur.states)
+ & HistoryItem.MOST_INTERESTING_STATES) == 0)) {
return;
}
}
@@ -4450,10 +4467,13 @@
}
public void commitPendingDataToDisk() {
- Parcel next;
+ final Parcel next;
synchronized (this) {
next = mPendingWrite;
mPendingWrite = null;
+ if (next == null) {
+ return;
+ }
mWriteLock.lock();
}
diff --git a/docs/html/guide/developing/debug-tasks.jd b/docs/html/guide/developing/debug-tasks.jd
index 500ef58..f0bf84c 100644
--- a/docs/html/guide/developing/debug-tasks.jd
+++ b/docs/html/guide/developing/debug-tasks.jd
@@ -40,10 +40,13 @@
your application, which can help you profile the performance of your application.</dd>
<dt><strong><a href="{@docRoot}guide/developing/tools/ddms.html#logcat">logcat</a></strong></dt>
<dd>Dumps a log of system
- messages. The messages include a stack trace when the emulator throws an error,
+ messages. The messages include a stack trace when the device throws an error,
as well as {@link android.util.Log} messages you've written from your application. To run
- logcat, execute <code>adb logcat</code> or, from DDMS, select <strong>Device > Run
- logcat</strong>.
+ logcat, execute <code>adb logcat</code> from your Android SDK {@code tools/} directory or,
+from DDMS, select <strong>Device > Run
+ logcat</strong>. When using the <a href="{@docRoot}sdk/eclipse-adt.html">ADT plugin for
+Eclipse</a>, you can also view logcat messages by opening the Logcat view, available from
+<strong>Window > Show View > Other > Android > Logcat</strong>.
<p>{@link android.util.Log} is a logging
class you can use to print out messages to the logcat. You can read messages
in real time if you run logcat on DDMS (covered next). Common logging methods include:
@@ -148,72 +151,7 @@
<h2 id="DebuggingWebPages">Debugging Web Pages</h2>
-<p>If you're developing a web application for Android devices, you can debug your JavaScript in the
-Android Browser using the Console APIs, which will output messages to logcat. If you're familiar
-debugging web pages with Firefox's FireBug or WebKit's Web Inspector, then you're probably familiar
-with the Console APIs. The Android Browser (and the {@link android.webkit.WebChromeClient}) supports
-most of the same APIs.</p>
-
-<p>When you call a function from the Console APIs (in the DOM's {@code window.console} object),
-you will see the output in logcat as a warning. For example, if your web page
-executes the following JavaScript:</p>
-<pre class="no-pretty-print">
-console.log("Hello World");
-</pre>
-<p>Then the logcat output from the Android Browser will look like this:</p>
-<pre class="no-pretty-print">
-W/browser ( 202): Console: Hello World http://www.example.com/hello.html :82
-</pre>
-
-<p>All Console messages from the Android Browser are tagged with the name "browser" on Android
-platforms running API Level 7 or higher. On platforms running API Level 6 or lower, Browser
-messages are tagged with the name "WebCore". The Android Browser also formats console messages
-with the log message
-preceded by "Console:" and then followed by the address and line number where the
-message occurred. (The format for the address and line number will appear different from the example
-above on platforms running API Level 6 or lower.)</p>
-
-<p>The Android Browser (and {@link android.webkit.WebChromeClient}) does not implement all of the
-Console APIs provided by Firefox or other WebKit-based browsers. Primarily, you need to depend
-on the basic text logging functions:</p>
-<ul>
- <li>{@code console.log(String)}</li>
- <li>{@code console.info(String)}</li>
- <li>{@code console.warn(String)}</li>
- <li>{@code console.error(String)}</li>
-</ul>
-<p>Although the Android Browser may not fully implement other Console functions, they will not raise
-run-time errors, but may not behave the same as they do on other desktop browsers.</p>
-
-<p>If you've implemented a custom {@link android.webkit.WebView} in your application, then in order
-to receive messages that are sent through the Console APIs, you must provide a {@link
-android.webkit.WebChromeClient} that implements the {@link
-android.webkit.WebChromeClient#onConsoleMessage(String,int,String) onConsoleMessage()} callback
-method. For example, assuming that the {@code myWebView} field references the {@link
-android.webkit.WebView} in your application, you can log debug messages like this:</p>
-<pre>
-myWebView.setWebChromeClient(new WebChromeClient() {
- public void onConsoleMessage(String message, int lineNumber, String sourceID) {
- Log.d("MyApplication", message + " -- From line " + lineNumber + " of " + sourceID);
- }
-});
-</pre>
-<p>The {@link android.webkit.WebChromeClient#onConsoleMessage(String,int,String)
-onConsoleMessage()} method will be called each time one of the Console methods is called from
-within your {@link android.webkit.WebView}.</p>
-<p>When the "Hello World" log is executed through your {@link android.webkit.WebView}, it will
-now look like this:</p>
-<pre class="no-pretty-print">
-D/MyApplication ( 430): Hello World -- From line 82 of http://www.example.com/hello.html
-</pre>
-
-<p class="note"><strong>Note:</strong> The {@link
-android.webkit.WebChromeClient#onConsoleMessage(String,int,String) onConsoleMessage()} callback
-method was added with API Level 7. If you are using a custom {@link
-android.webkit.WebView} on a platform running API Level 6 or lower, then your Console messages will
-automatically be sent to logcat with the "WebCore" logging tag.</p>
-
-
+<p>See the <a href="{@docRoot}guide/webapps/debugging.html">Debugging Web Apps</a> document.</p>
<h2 id="toptips">Top Debugging Tips</h2>
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index 43a5e8d..cdf5feb 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -110,7 +110,7 @@
<li class="toggle-list">
<div><a href="<?cs var:toroot ?>guide/topics/resources/index.html">
<span class="en">Application Resources</span>
- </a> <span class="new">new!</span></div>
+ </a></div>
<ul>
<li><a href="<?cs var:toroot ?>guide/topics/resources/providing-resources.html">
<span class="en">Providing Resources</span>
@@ -120,14 +120,14 @@
</a></li>
<li><a href="<?cs var:toroot ?>guide/topics/resources/runtime-changes.html">
<span class="en">Handling Runtime Changes</span>
- </a> <span class="new">new!</span></li>
+ </a></li>
<li><a href="<?cs var:toroot ?>guide/topics/resources/localization.html">
<span class="en">Localization</span>
</a></li>
<li class="toggle-list">
<div><a href="<?cs var:toroot ?>guide/topics/resources/available-resources.html">
<span class="en">Resource Types</span>
- </a> <span class="new">new!</span></div>
+ </a></div>
<ul>
<li><a href="<?cs var:toroot ?>guide/topics/resources/animation-resource.html">Animation</a></li>
<li><a href="<?cs var:toroot ?>guide/topics/resources/color-list-resource.html">Color State List</a></li>
@@ -147,11 +147,11 @@
<li class="toggle-list">
<div><a href="<?cs var:toroot ?>guide/topics/data/data-storage.html">
<span class="en">Data Storage</span>
- </a> <span class="new">new!</span></div>
+ </a></div>
<ul>
<li><a href="<?cs var:toroot ?>guide/topics/data/backup.html">
<span class="en">Data Backup</span>
- </a> <span class="new">new!</span>
+ </a>
</li>
</ul>
</li>
@@ -227,7 +227,7 @@
<ul>
<li><a href="<?cs var:toroot ?>guide/topics/location/obtaining-user-location.html">
<span class="en">Obtaining User Location</span>
- </a> <span class="new">new!</span></li>
+ </a></li>
</ul>
</li>
<!--<li class="toggle-list">
@@ -246,7 +246,7 @@
<li class="toggle-list">
<div><a href="<?cs var:toroot?>guide/topics/search/index.html">
<span class="en">Search</span>
- </a> <span class="new">new!</span></div>
+ </a></div>
<ul>
<li><a href="<?cs var:toroot?>guide/topics/search/search-dialog.html">Using the Android Search Dialog</a></li>
<li><a href="<?cs var:toroot?>guide/topics/search/adding-recent-query-suggestions.html">Adding Recent Query Suggestions</a></li>
@@ -255,11 +255,11 @@
</ul>
</li>
<li><a href="<?cs var:toroot?>guide/topics/testing/testing_android.html">
- <span class="en">Testing and Instrumentation</span></a>
- <span class="new">new!</span></li>
+ <span class="en">Testing and Instrumentation</span>
+ </a></li>
<li><a href="<?cs var:toroot?>guide/topics/admin/device-admin.html">
- <span class="en">Device Administration</span></a>
- <span class="new">new!</span></li>
+ <span class="en">Device Administration</span>
+ </a> <span class="new">new!</span><!-- 10/8/10 --></li>
</ul>
</li>
@@ -306,18 +306,18 @@
<div>
<a href="<?cs var:toroot ?>guide/developing/testing/index.html">
<span class="en">Testing</span>
- </a> <span class="new">new!</span>
+ </a>
</div>
<ul>
<li>
<a href="<?cs var:toroot ?>guide/developing/testing/testing_eclipse.html">
<span class="en">Testing in Eclipse, with ADT</span>
- </a> <span class="new">new!</span>
+ </a>
</li>
<li>
<a href="<?cs var:toroot ?>guide/developing/testing/testing_otheride.html">
<span class="en">Testing in Other IDEs</span>
- </a> <span class="new">new!</span>
+ </a>
</li>
</ul>
</li>
@@ -332,8 +332,7 @@
<!--<li><a href="<?cs var:toroot ?>guide/developing/tools/adt.html">ADT Plugin</a></li>-->
<li><a href="<?cs var:toroot ?>guide/developing/tools/aidl.html">aidl</a></li>
<li><a href="<?cs var:toroot ?>guide/developing/tools/avd.html">AVDs</a></li>
- <li><a href="<?cs var:toroot ?>guide/developing/tools/bmgr.html">bmgr</a>
- <span class="new">new!</span></li>
+ <li><a href="<?cs var:toroot ?>guide/developing/tools/bmgr.html">bmgr</a></li>
<li><a href="<?cs var:toroot ?>guide/developing/tools/ddms.html">ddms</a></li>
<li><a href="<?cs var:toroot ?>guide/developing/tools/othertools.html#dx">dx</a></li>
<li><a href="<?cs var:toroot ?>guide/developing/tools/draw9patch.html">Draw 9-Patch</a></li>
@@ -413,7 +412,7 @@
<ul>
<li><a href="<?cs var:toroot ?>guide/practices/compatibility.html">
<span class="en">Compatibility</span>
- </a><span class="new">new!</span></li>
+ </a></li>
<li><a href="<?cs var:toroot ?>guide/practices/screens_support.html">
<span class="en">Supporting Multiple Screens</span>
</a></li>
@@ -451,6 +450,25 @@
</li>
<li>
+ <h2><span class="en">Web Applications</span>
+ </h2>
+ <ul>
+ <li><a href="<?cs var:toroot ?>guide/webapps/targetting.html">
+ <span class="en">Targetting Android Devices</span>
+ </a> <span class="new">new!</span><!-- 10/8/10 --></li>
+ <li><a href="<?cs var:toroot ?>guide/webapps/webview.html">
+ <span class="en">Building Web Apps in WebView</span>
+ </a> <span class="new">new!</span><!-- 10/8/10 --></li>
+ <li><a href="<?cs var:toroot ?>guide/webapps/debugging.html">
+ <span class="en">Debugging Web Apps</span>
+ </a> <span class="new">new!</span><!-- 10/8/10 --></li>
+ <li><a href="<?cs var:toroot ?>guide/webapps/best-practices.html">
+ <span class="en">Best Practices for Web Apps</span>
+ </a> <span class="new">new!</span><!-- 10/8/10 --></li>
+ </ul>
+ </li>
+
+ <li>
<h2><span class="en">Appendix</span>
<span class="de" style="display:none">Anhang</span>
<span class="es" style="display:none">Apéndice</span>
@@ -466,10 +484,10 @@
</a></li>
<li><a href="<?cs var:toroot ?>guide/appendix/market-filters.html">
<span class="en">Market Filters</span>
- </a> <span class="new">new!</span></li>
+ </a></li>
<li><a href="<?cs var:toroot ?>guide/appendix/install-location.html">
<span class="en">App Install Location</span>
- </a> <span class="new">new!</span></li>
+ </a></li>
<li><a href="<?cs var:toroot ?>guide/appendix/media-formats.html">
<span class="en">Supported Media Formats</span>
</a></li>
diff --git a/docs/html/guide/topics/admin/device-admin.jd b/docs/html/guide/topics/admin/device-admin.jd
index 4d9a14f..fda716a 100644
--- a/docs/html/guide/topics/admin/device-admin.jd
+++ b/docs/html/guide/topics/admin/device-admin.jd
@@ -1,24 +1,33 @@
-page.title=Android Device Administration API
+page.title=Device Administration
@jd:body
+
<div id="qv-wrapper">
<div id="qv">
<h2>In this document</h2>
<ol>
<li><a href="#overview">Device Administration API Overview</a>
<ol>
+ <li><a href="#how">How does it work?</a></li>
<li><a href="#policies">Policies</a></li>
</ol>
</li>
- <li><a href="#how">How Does It Work?</a></li>
<li><a href="#sample">Sample Application</a></li>
<li><a href="#developing">Developing a Device Administration Application</a>
<ol>
- <li><a href="#manifest">Creating the Manifest</a></li>
- <li><a href="#code">Implementing the Code</a>
- </li>
+ <li><a href="#manifest">Creating the manifest</a></li>
+ <li><a href="#code">Implementing the code</a></li>
</ol>
+ </li>
+
+ </ol>
+
+ <h2>Key classes</h2>
+ <ol>
+ <li>{@link android.app.admin.DeviceAdminReceiver}</li>
+ <li>{@link android.app.admin.DevicePolicyManager}</li>
+ <li>{@link android.app.admin.DeviceAdminInfo}</li>
</ol>
- </div>
+</div>
</div>
<p>Android 2.2 introduces support for enterprise applications by offering the
@@ -37,7 +46,10 @@
solutions for Android-powered devices. It discusses the various features
provided by the Device Administration API to provide stronger security for
employee devices that are powered by Android.</p>
+
+
<h2 id="overview">Device Administration API Overview</h2>
+
<p>Here are examples of the types of applications that might use the Device Administration API:</p>
<ul>
<li>Email clients.</li>
@@ -45,14 +57,14 @@
<li>Device management services and applications.</li>
</ul>
-<h3 id="how">How Does it Work?</h3>
+<h3 id="how">How does it work?</h3>
<p>You use the Device Administration API to write device admin applications that users
install on their devices. The device admin application enforces the desired
policies. Here's how it works:</p> <ul>
<li>A system administrator writes a device admin application that enforces
remote/local device security policies. These policies could be hard-coded into
the app, or the application could dynamically fetch policies from a third-party
-server. </li>
+server. </li>
<li>The application is installed on users' devices. Android does
not currently have an automated provisioning solution. Some of the ways a sysadmin might
distribute the application to users are as follows:
@@ -68,7 +80,7 @@
<li>The system prompts the user to enable the device admin application. How
and when this happens depends on how the application is implemented.</li>
<li>Once users enable the device admin application, they are subject to
-its policies. Complying with those policies typically confers benefits, such as
+its policies. Complying with those policies typically confers benefits, such as
access to sensitive systems and data.</li>
</ul>
<p>If users do not enable the device admin app, it remains on the device, but in an inactive state. Users will not be subject to its policies, and they will conversely not get any of the application's benefits—for example, they may not be able to sync data.</p>
@@ -88,12 +100,14 @@
<p>To uninstall an existing device admin application, users need to
first unregister the application as an administrator. </p>
-<h3 id ="policies">Policies</h3>
+
+<h3 id="policies">Policies</h3>
+
<p>In an enterprise setting, it's often the case that employee devices must
adhere to a strict set of policies that govern the use of the device. The
Device Administration API supports the policies listed in Table 1.
Note that the Device Administration API currently only supports passwords for screen
-lock:</p>
+lock:</p>
<p class="table-caption"><strong>Table 1.</strong> Policies supported by the Device Administration API.</p>
<table border="1">
<tr>
@@ -109,7 +123,7 @@
<td>Set the required number of characters for the password. For example, you
can require PIN or passwords to have at least six characters. </td> </tr>
<tr>
- <td>Alphanumeric password required</td>
+ <td>Alphanumeric password required</td>
<td>Requires that passwords have a
combination of letters and numbers. They may include symbolic characters.
</td>
@@ -128,7 +142,9 @@
need to enter their PIN or passwords again before they can use their devices and
access data. The value can be between 1 and 60 minutes.</td> </tr>
</table>
-<h4>Other Features</h4>
+
+<h4>Other features</h4>
+
<p>In addition to supporting the policies listed in the above table, the Device
Administration API lets you do the following:</p> <ul>
<li>Prompt user to set a new password.</li>
@@ -138,6 +154,7 @@
<h2 id="sample">Sample Application</h2>
+
<p>The examples used in this document are based on the <a
href="{@docRoot}resources/samples/ApiDemos/src/com/example/
android/apis/app/DeviceAdminSample.html">Device Administration API
@@ -147,7 +164,7 @@
Getting the Samples</a>. Here is the <a
href="{@docRoot}resources/samples/ApiDemos/src/com/example/
android/apis/app/DeviceAdminSample.html">complete code</a> for
-the sample. </p>
+the sample. </p>
<p>The
sample application offers a demo of device admin features. It presents users
with a user interface that lets them enable the device admin application. Once
@@ -169,13 +186,17 @@
<img src="{@docRoot}images/admin/device-admin-app.png"/>
<p class="img-caption"><strong>Figure 1.</strong> Screenshot of the Sample Application</p>
+
+
<h2 id="developing">Developing a Device Administration Application</h2>
<p>System administrators can use the Device Administration API to write an application
that enforces remote/local device security policy enforcement. This section
summarizes the steps involved in creating a device administration
application.</p>
-<h3 id="manifest">Creating the Manifest</h3>
+
+<h3 id="manifest">Creating the manifest</h3>
+
<p>To use the Device Administration API, the application's
manifest must include the following:</p>
<ul>
@@ -207,7 +228,7 @@
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver></pre>
-
+
<p>Note that:</p>
<ul>
<li>The activity in the sample application is an {@link android.app.Activity}
@@ -218,7 +239,7 @@
an inner class; it just is in this example.</li>
<li>The following attributes refer to string resources that for the sample application reside in
-<code>ApiDemos/res/values/strings.xml</code>. For more information about resources, see
+<code>ApiDemos/res/values/strings.xml</code>. For more information about resources, see
<a
href="{@docRoot}guide/topics/resources/index.html">Application Resources</a>.
<ul>
@@ -234,8 +255,8 @@
a label.</li>
</ul>
-
-<li><code>android:permission="android.permission.BIND_DEVICE_ADMIN"
+
+<li><code>android:permission="android.permission.BIND_DEVICE_ADMIN"
</code> is a permission that a {@link android.app.admin.DeviceAdminReceiver} subclass must
have, to ensure that only the system can interact with the receiver (no application can be granted this permission). This
prevents other applications from abusing your device admin app.</li>
@@ -273,11 +294,15 @@
include all of the policies, just the ones that are relevant for your app.
</p>
For more discussion of the manifest file, see the <a
-href="{@docRoot}guide/topics/manifest/manifest-intro.html">Android Developers Guide</a>.
-<h2 id="code">Implementing the Code</h2>
+href="{@docRoot}guide/topics/manifest/manifest-intro.html">Android Developers Guide</a>.
+
+
+
+<h3 id="code">Implementing the code</h3>
+
<p>The Device Administration API includes the following classes:</p>
<dl>
- <dt>{@link android.app.admin.DeviceAdminReceiver}</dt>
+ <dt>{@link android.app.admin.DeviceAdminReceiver}</dt>
<dd>Base class for implementing a device administration component. This class provides
a convenience for interpreting the raw intent actions that are sent by the
system. Your Device Administration application must include a
@@ -287,7 +312,7 @@
this class must have published a {@link android.app.admin.DeviceAdminReceiver} that the user
has currently enabled. The {@link android.app.admin.DevicePolicyManager} manages policies for
one or more {@link android.app.admin.DeviceAdminReceiver} instances</dd>
- <dt>{@link android.app.admin.DeviceAdminInfo}</dt>
+ <dt>{@link android.app.admin.DeviceAdminInfo}</dt>
<dd>This class is used to specify metadata
for a device administrator component.</dd>
</dl>
@@ -295,6 +320,7 @@
The rest of this section describes how you use the {@link
android.app.admin.DeviceAdminReceiver} and
{@link android.app.admin.DevicePolicyManager} APIs to write a device admin application.</p>
+
<h4 id="receiver">Subclassing DeviceAdminReceiver</h4>
<p>To create a device admin application, you must subclass
{@link android.app.admin.DeviceAdminReceiver}. The {@link android.app.admin.DeviceAdminReceiver} class
@@ -305,7 +331,7 @@
events. For example:</p>
<pre>public class DeviceAdminSample extends DeviceAdminReceiver {
-...
+...
@Override
public void onEnabled(Context context, Intent intent) {
showToast(context, "Sample Device Admin: enabled");
@@ -331,30 +357,32 @@
}
...
}</pre>
-<h4 id="enabling">Enabling the Application</h4>
+
+<h4 id="enabling">Enabling the application</h4>
<p>One of the major events a device admin application has to handle is the user
enabling the application. The user must explicitly enable the application for
the policies to be enforced. If the user chooses not to enable the application
it will still be present on the device, but its policies will not be enforced, and the user will not
get any of the application's benefits.</p>
<p>The process of enabling the application begins when the user performs an
-action that triggers the {@link android.app.admin.DevicePolicyManager#ACTION_ADD_DEVICE_ADMIN}
+action that triggers the {@link android.app.admin.DevicePolicyManager#ACTION_ADD_DEVICE_ADMIN}
intent. In the
sample application, this happens when the user clicks the <strong>Enable
Admin</strong> button. </p>
<p>When the user clicks the <strong>Enable Admin</strong> button, the display
-changes to prompt the user to enable the device admin application, as shown in <strong>Figure 2</strong>.</p>
+changes to prompt the user to enable the device admin application, as shown in figure
+2.</p>
<img src="{@docRoot}images/admin/device-admin-activate-prompt.png"/>
<p class="img-caption"><strong>Figure 2.</strong> Sample Application: Activating the Application</p>
<p>Below is the code that gets executed when the user clicks the <strong>Enable
-Admin</strong> button shown in <strong>Figure 1</strong>. </p>
+Admin</strong> button shown in figure 1. </p>
<pre> private OnClickListener mEnableListener = new OnClickListener() {
public void onClick(View v) {
// Launch the activity to have the user enable our admin.
Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
- intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
+ intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
mDeviceAdminSample);
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION,
"Additional text explaining why this needs to be added.");
@@ -379,10 +407,10 @@
}</pre>
<p>The line
-<code>intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
+<code>intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
mDeviceAdminSample)</code> states that <code>mDeviceAdminSample</code> (which is
a {@link android.app.admin.DeviceAdminReceiver} component) is the target policy.
-This line invokes the user interface shown in <strong>Figure 2</strong>, which guides users through
+This line invokes the user interface shown in figure 2, which guides users through
adding the device administrator to the system (or allows them to reject it).</p>
<p>When the application needs to perform an operation that is contingent on the
@@ -402,14 +430,16 @@
// do something else
}
</pre>
-<h3 id="admin_ops">Managing Policies</h3>
+
+<h3 id="admin_ops">Managing policies</h3>
<p>{@link android.app.admin.DevicePolicyManager} is a public class for managing policies
enforced on a device. {@link android.app.admin.DevicePolicyManager} manages policies for one
or more {@link android.app.admin.DeviceAdminReceiver} instances. </p>
<p>You get a handle to the {@link android.app.admin.DevicePolicyManager} as follows: </p>
-<pre>DevicePolicyManager mDPM =
-(DevicePolicyManager)getSystemService(Context.DEVICE_POLICY_SERVICE);<br
-/></pre>
+<pre>
+DevicePolicyManager mDPM =
+ (DevicePolicyManager)getSystemService(Context.DEVICE_POLICY_SERVICE);
+</pre>
<p>This section describes how to use {@link android.app.admin.DevicePolicyManager} to perform
administrative tasks:</p>
<ul>
@@ -417,26 +447,29 @@
<li><a href="#lock">Set device lock</a></li>
<li><a href="#wipe">Perform data wipe</a></li>
</ul>
+
<h4 id="pwd">Set password policies</h4>
<p>{@link android.app.admin.DevicePolicyManager} includes APIs for setting and enforcing the
device password policy. In the Device Administration API, the password only applies to
screen lock. This section describes common password-related tasks.</p>
+
<h5>Set a password for the device</h5>
<p>This code displays a user interface prompting the user to set a password:</p>
<pre>Intent intent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD);
startActivity(intent);
</pre>
+
<h5>Set the password quality</h5>
<p>The password quality can be one of the following {@link android.app.admin.DevicePolicyManager} constants: </p>
<dl>
- <dt>{@link android.app.admin.DevicePolicyManager#PASSWORD_QUALITY_ALPHABETIC}</dt><dd>The user must enter a
+ <dt>{@link android.app.admin.DevicePolicyManager#PASSWORD_QUALITY_ALPHABETIC}</dt><dd>The user must enter a
password containing at least alphabetic (or other symbol) characters.</dd>
- <dt>{@link android.app.admin.DevicePolicyManager#PASSWORD_QUALITY_ALPHANUMERIC}</dt><dd>The user must enter a
+ <dt>{@link android.app.admin.DevicePolicyManager#PASSWORD_QUALITY_ALPHANUMERIC}</dt><dd>The user must enter a
password containing at least <em>both</em> numeric <em>and</em> alphabetic (or
other symbol) characters.</dd>
<dt>{@link android.app.admin.DevicePolicyManager#PASSWORD_QUALITY_NUMERIC}</dt><dd>The user must enter a password
containing at least numeric characters.</dd>
- <dt>{@link android.app.admin.DevicePolicyManager#PASSWORD_QUALITY_SOMETHING}</dt><dd>The policy requires some kind
+ <dt>{@link android.app.admin.DevicePolicyManager#PASSWORD_QUALITY_SOMETHING}</dt><dd>The policy requires some kind
of password, but doesn't care what it is.</dd>
<dt>{@link android.app.admin.DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED}</dt><dd>
The policy has no requirements for the password. </dd>
@@ -448,6 +481,7 @@
...
mDPM.setPasswordQuality(mDeviceAdminSample, DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC);
</pre>
+
<h5>Set the minimum password length</h5>
<p>You can specify that a password must be at least the specified minimum
length. For example:</p>
@@ -457,19 +491,21 @@
...
mDPM.setPasswordMinimumLength(mDeviceAdminSample, pwLength);
</pre>
+
<h5>Set maximum failed password attempts</h5>
<p>You can set the maximum number of allowed failed password attempts before the
device is wiped (that is, reset to factory settings). For example:</p>
-<pre>DevicePolicyManager mDPM;
-ComponentName mDeviceAdminSample;
+<pre>DevicePolicyManager mDPM;
+ComponentName mDeviceAdminSample;
int maxFailedPw;
...
mDPM.setMaximumFailedPasswordsForWipe(mDeviceAdminSample, maxFailedPw);</pre>
+
<h4 id="lock">Set device lock</h4>
<p>You can set the maximum period of user inactivity that can occur before the
device locks. For example:</p>
<pre>
-DevicePolicyManager mDPM;
+DevicePolicyManager mDPM;
ComponentName mDeviceAdminSample;
...
long timeMs = 1000L*Long.parseLong(mTimeout.getText().toString());
@@ -477,9 +513,11 @@
</pre>
<p>You can also programmatically tell the device to lock immediately:</p>
<pre>
-DevicePolicyManager mDPM;
+DevicePolicyManager mDPM;
mDPM.lockNow();</pre>
+
<h4 id="wipe">Perform data wipe</h4>
+
<p>You can use the {@link android.app.admin.DevicePolicyManager} method
{@link android.app.admin.DevicePolicyManager#wipeData wipeData()} to reset the device to factory settings. This is useful
if the device is lost or stolen. Often the decision to wipe the device is the
@@ -488,7 +526,7 @@
wiped after a specific number of failed password attempts.</p>
<p>You wipe data as follows:</p>
<pre>
-DevicePolicyManager mDPM;
+DevicePolicyManager mDPM;
mDPM.wipeData(0);</pre>
<p>The {@link android.app.admin.DevicePolicyManager#wipeData wipeData()} method takes as its parameter a bit mask of
additional options. Currently the value must be 0. </p>
diff --git a/docs/html/guide/webapps/best-practices.jd b/docs/html/guide/webapps/best-practices.jd
new file mode 100644
index 0000000..1bde5bf
--- /dev/null
+++ b/docs/html/guide/webapps/best-practices.jd
@@ -0,0 +1,90 @@
+page.title=Best Practices for Web Apps
+@jd:body
+
+<style>
+.bold li {
+ font-weight:bold;
+}
+.bold li * {
+ font-weight:normal;
+}
+</style>
+
+<p>Developing web pages and web applications for mobile devices presents a different set of
+challenges compared to developing a web page for the typical
+desktop web browser. To help you get started, the following is a list of practices you should
+follow in order to provide the most effective web application for Android and other mobile
+devices.</p>
+
+<ol class="bold">
+
+<li>Redirect mobile devices to a dedicated mobile version of your web site
+ <p>There are several ways you can redirect requests to the mobile version of your web site, using
+server-side redirects. Most often, this is done by "sniffing" the User Agent
+string provided by the web browser. To determine whether to serve a mobile version of your site, you
+should simply look for the "mobile" string in the User Agent, which matches a wide variety of mobile
+devices. If necessary, you can also identify the specific operating system in the User Agent string
+(such as "Android 2.1").</p>
+</li>
+
+
+<li>Use a valid markup DOCTYPE that's appropriate for mobile devices
+ <p>The most common markup language used for mobile web sites is <a
+href="http://www.w3.org/TR/2008/REC-xhtml-basic-20080729/">XHTML Basic</a>. This standard
+ensures specific markup for your web site that works best on mobile devices. For instance, it does
+not allow HTML frames or nested tables, which perform poorly on mobile devices. Along with the
+DOCTYPE, be sure to declare the appropriate character encoding for the document (such as
+UTF-8).</p>
+ <p>For example:</p>
+<pre>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN"
+ "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">
+</pre>
+
+ <p>Also be sure that your web page markup is valid against the declared DOCTYPE. Use a
+validator, such as the one available at
+<a href="http://validator.w3.org/">http://validator.w3.org</a>.</p>
+</li>
+
+
+<li>Use viewport meta data to properly resize your web page
+ <p>In your document {@code <head>}, you should provide meta data that specifies how you
+want the browser's viewport to render your web page. For example, your viewport meta data can
+specify the height and width for the browser's viewport, the initial web page scale and even the
+target screen density.</p>
+ <p>For example:</p>
+<pre>
+<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
+</pre>
+ <p>For more information about how to use viewport meta data for Android-powered devices, read <a
+href="{@docRoot}guide/webapps/targetting.html">Targetting Android Devices</a>.</p>
+</li>
+
+
+<li>Avoid multiple file requests
+ <p>Because mobile devices typically have a connection speed far slower than a desktop
+computer, you should make your web pages load as fast as possible. One way to speed it up is to
+avoid loading extra files such as stylesheets and script files in the {@code
+<head>}. Instead, provide your CSS and JavaScript directly in the <head> (or
+at the end of the <body>, for scripts that you don't need until the page is loaded).
+Alternatively, you should optimize the size and speed of your files by compressing them with tools
+like <a href="http://code.google.com/p/minify/">Minify</a>.</p>
+</li>
+
+
+<li>Use a vertical linear layout
+ <p>Avoid the need for the user to scroll left and right while navigating your web
+page. Scrolling up and down is easier for the user and makes your web page simpler.</p>
+</li>
+
+</ol>
+
+<p>For a more thorough guide to creating great mobile web applications, see the W3C's <a
+href="http://www.w3.org/TR/mobile-bp/">Mobile Web Best Practices</a>. For other guidance on
+improving the speed of your web site (for mobile and desktop), see Yahoo!'s guide to <a
+href="http://developer.yahoo.com/performance/index.html#rules">Exceptional Performance</a> and
+Google's speed tutorials in <a href="http://code.google.com/speed/articles/">Let's make the web
+faster</a>.</p>
+
+
diff --git a/docs/html/guide/webapps/debugging.jd b/docs/html/guide/webapps/debugging.jd
new file mode 100644
index 0000000..098e17c
--- /dev/null
+++ b/docs/html/guide/webapps/debugging.jd
@@ -0,0 +1,158 @@
+page.title=Debugging Web Apps
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+<h2>Quickview</h2>
+<ul>
+ <li>You can debug your web app using console methods in JavaScript</li>
+ <li>If debugging in a custom WebView, you need to implement a callback method to handle debug
+messages</li>
+</ul>
+
+<h2>In this document</h2>
+<ol>
+ <li><a href="#Browser">Using Console APIs in the Android Browser</a></li>
+ <li><a href="#WebView">Using Console APIs in WebView</a></li>
+</ol>
+
+<h2>See also</h2>
+<ol>
+ <li><a href="{@docRoot}guide/developing/debug-tasks.html">Debugging Tasks</a></li>
+</ol>
+
+</div>
+</div>
+
+<p>If you're developing a web application for Android, you can debug your JavaScript
+using the {@code console} JavaScript APIs, which output messages to logcat. If you're familiar with
+debugging web pages with Firebug or Web Inspector, then you're probably familiar
+with using {@code console} (such as {@code console.log()}). Android's WebKit framework supports most
+of the same APIs, so you can receive logs from your web page when debugging in Android's Browser
+or in your own {@link android.webkit.WebView}.</p>
+
+
+
+<h2 id="Browser">Using Console APIs in the Android Browser</h2>
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+ <h2>Logcat</h2>
+ <p>Logcat is a tool that dumps a log of system messages. The messages include a stack trace when
+the device throws an error, as well as log messages written from your application and
+those written using JavaScript {@code console} APIs.</p>
+ <p>To run logcat and view messages, execute
+{@code adb logcat} from your Android SDK {@code tools/} directory, or, from DDMS, select
+<strong>Device > Run logcat</strong>. When using the <a href="{@docRoot}sdk/eclipse-adt.html">ADT
+plugin for Eclipse</a>, you can also view logcat messages by opening the Logcat view, available from
+<strong>Window > Show View > Other > Android > Logcat</strong>.</p>
+ <p>See <a href="{@docRoot}guide/developing/debug-tasks.html">Debugging
+Tasks</a> for more information about logcat.</p>
+</div>
+</div>
+
+<p>When you call a {@code console} function (in the DOM's {@code window.console} object),
+the output appears in logcat. For example, if your web page executes the following
+JavaScript:</p>
+<pre>
+console.log("Hello World");
+</pre>
+<p>Then the logcat message looks something like this:</p>
+<pre class="no-pretty-print">
+Console: Hello World http://www.example.com/hello.html :82
+</pre>
+
+<p>The format of the message might appear different depending on which version of Android you're
+using. On Android 2.1 and higher, console messages from the Android Browser
+are tagged with the name "browser". On Android 1.6 and lower, Android Browser
+messages are tagged with the name "WebCore".</p>
+
+<p>Android's WebKit does not implement all of the console APIs available in other desktop browsers.
+You can, however, use the basic text logging functions:</p>
+<ul>
+ <li>{@code console.log(String)}</li>
+ <li>{@code console.info(String)}</li>
+ <li>{@code console.warn(String)}</li>
+ <li>{@code console.error(String)}</li>
+</ul>
+
+<p>Other console functions don't raise errors, but might not behave the same as what you
+expect from other web browsers.</p>
+
+
+
+<h2 id="WebView">Using Console APIs in WebView</h2>
+
+<p>If you've implemented a custom {@link android.webkit.WebView} in your application, all the
+same console APIs are supported when debugging your web page in WebView. On Android
+1.6 and lower, console messages are automatically sent to logcat with the
+"WebCore" logging tag. If you're targetting Android 2.1 (API Level 7) or higher, then you must
+provide a {@link android.webkit.WebChromeClient}
+that implements the {@link android.webkit.WebChromeClient#onConsoleMessage(String,int,String)
+onConsoleMessage()} callback method, in order for console messages to appear in logcat.</p>
+
+<p>Additionally, the {@link
+android.webkit.WebChromeClient#onConsoleMessage(String,int,String)} method introduced in API
+Level 7 has been deprecated in favor of {@link
+android.webkit.WebChromeClient#onConsoleMessage(ConsoleMessage)} in API Level 8.</p>
+
+<p>Whether you're developing for Android 2.1 (API Level 7) or Android 2.2 (API Level 8 or
+greater), you must implement {@link android.webkit.WebChromeClient} and override the appropriate
+{@link
+android.webkit.WebChromeClient#onConsoleMessage(String,int,String) onConsoleMessage()} callback
+method. Then, apply the {@link android.webkit.WebChromeClient} to your {@link
+android.webkit.WebView} with {@link android.webkit.WebView#setWebChromeClient(WebChromeClient)
+setWebChromeClient()}.
+
+<p>Using API Level 7, this is how your code for {@link
+android.webkit.WebChromeClient#onConsoleMessage(String,int,String)} might look:</p>
+
+<pre>
+WebView myWebView = (WebView) findViewById(R.id.webview);
+myWebView.setWebChromeClient(new WebChromeClient() {
+ public void onConsoleMessage(String message, int lineNumber, String sourceID) {
+ Log.d("MyApplication", message + " -- From line "
+ + lineNumber + " of "
+ + sourceID);
+ }
+});
+</pre>
+
+<p>With API Level 8 or greater, your code for {@link
+android.webkit.WebChromeClient#onConsoleMessage(ConsoleMessage)} might look like this:</p>
+
+<pre>
+WebView myWebView = (WebView) findViewById(R.id.webview);
+myWebView.setWebChromeClient(new WebChromeClient() {
+ public boolean onConsoleMessage(ConsoleMessage cm) {
+ Log.d("MyApplication", cm.{@link android.webkit.ConsoleMessage#message()} + " -- From line "
+ + cm.{@link android.webkit.ConsoleMessage#lineNumber()} + " of "
+ + cm.{@link android.webkit.ConsoleMessage#sourceId()} );
+ return true;
+ }
+});
+</pre>
+
+<p>The {@link android.webkit.ConsoleMessage} also includes a {@link
+android.webkit.ConsoleMessage.MessageLevel MessageLevel} to indicate the type of console message
+being delivered. You can query the message level with {@link
+android.webkit.ConsoleMessage#messageLevel()} to determine the severity of the message, then
+use the appropriate {@link android.util.Log} method or take other appropriate actions.</p>
+
+<p>Whether you're using {@link
+android.webkit.WebChromeClient#onConsoleMessage(String,int,String)} or {@link
+android.webkit.WebChromeClient#onConsoleMessage(ConsoleMessage)}, when you execute a console method
+in your web page, Android calls the appropriate {@link
+android.webkit.WebChromeClient#onConsoleMessage(String,int,String)
+onConsoleMessage()} method so you can report the error. For example, with the example code above,
+a logcat message is printed that looks like this:</p>
+
+<pre class="no-pretty-print">
+Hello World -- From line 82 of http://www.example.com/hello.html
+</pre>
+
+
+
+
+
+
diff --git a/docs/html/guide/webapps/targetting.jd b/docs/html/guide/webapps/targetting.jd
new file mode 100644
index 0000000..844b9ca
--- /dev/null
+++ b/docs/html/guide/webapps/targetting.jd
@@ -0,0 +1,419 @@
+page.title=Targetting Android Devices
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+<h2>Quickview</h2>
+<ul>
+ <li>You can target your web page for different screens using viewport metadata, CSS, and
+JavaScript</li>
+ <li>Techniques in this document work for Android 2.0 and greater</li>
+</ul>
+
+<h2>In this document</h2>
+<ol>
+<li><a href="#Metadata">Using Viewport Metadata</a>
+ <ol>
+ <li><a href="#ViewportSize">Defining the viewport size</a></li>
+ <li><a href="#ViewportScale">Defining the viewport scale</a></li>
+ <li><a href="#ViewportDensity">Defining the viewport target density</a></li>
+ </ol>
+</li>
+<li><a href="#DensityCSS">Targetting Device Density with CSS</a></li>
+<li><a href="#DensityJS">Targetting Device Density with JavaScript</a></li>
+</ol>
+
+</div>
+</div>
+
+
+<p>If you're developing a web application for Android or redesigning one for mobile devices, you
+should account for some factors that affect the way the Android Browser renders your web page by
+default. There are two fundamental factors that you should account for:</p>
+
+<dl>
+ <dt>The size of the viewport and scale of the web page</dt>
+ <dd>When the Android Browser loads a web page, the default behavior is to load the
+page in "overview mode," which provides a zoomed-out perspective of the web page. You can override
+this behavior for your web page by defining the default dimensions of the viewport or the initial
+scale of the viewport. You can also control how much the user can zoom in and out of your web
+page, if at all.
+ <p>However, the user can also disable overview mode in the
+Browser settings, so you should not assume that your page will load in overview mode. You
+should instead customize the viewport size and/or scale as appropriate for your page.</p></dd>
+
+ <dt>The device's screen density</dt>
+ <dd>The screen density (the number of pixels per inch) on an Android-powered device affects
+the resolution and size at which a web page is displayed. (There are three screen density
+categories: low, medium, and high.) The Android Browser compensates for variations in the screen
+density by scaling a web page so that all devices display the web page at the same perceivable size
+as a medium-density screen. If graphics are an important element of your web design, you
+should pay close attention to the scaling that occurs on different densities, because image scaling
+can produce artifacts (blurring and pixelation).
+ <p>To provide the best visual representation on all
+screen densities, you should control how scaling occurs by providing viewport metadata about
+your web page's target screen density and providing alternative graphics for different screen
+densities, which you can apply to different screens using CSS or JavaScript.</p></dd>
+</dl>
+
+<p>The rest of this document describes how you can account for these effects, and how to target
+your web page for specific screen configurations.</p>
+
+<p class="note"><strong>Note:</strong> The features described in this document are supported
+by the Android Browser application on Android 2.0 and greater. Third-party web browsers running on
+Android might not support these techniques for controlling the viewport size and targetting
+screen densities.</p>
+
+
+
+<h2 id="Metadata">Using Viewport Metadata</h2>
+
+<p>The viewport is the area in which the Android Browser
+draws a web page. Although the viewport's visible area matches the size of the screen,
+the viewport has its own dimensions that determine the number of pixels available to a web page.
+That is, the number of pixels available to a web page before it exceeds the screen area is
+defined by the dimensions of the viewport,
+not the dimensions of the device screen. For example, although a device screen might have a width of
+480 pixels, the viewport can have a width of 800 pixels, so that a web page designed to be 800
+pixels wide is completely visible on the screen.</p>
+
+<p>You can define properties of the viewport for your web page using the {@code "viewport"}
+property in an HTML {@code <meta>} tag (which must
+be placed in your document {@code <head>}). You can define multiple viewport properties in the
+{@code <meta>} tag's {@code content} attribute. For example, you can define the height and
+width of the viewport, the initial scale of the page, and the target screen density.
+Each viewport property in the {@code content} attribute must be separated by a comma.</p>
+
+<p>For example, the following snippet from an HTML document specifies that the viewport width
+should exactly match the device screen width and that the ability to zoom should be disabled:</p>
+
+<pre>
+<head>
+ <title>Example</title>
+ <meta name="viewport" content="width=device-width, user-scalable=no" />
+</head>
+</pre>
+
+<p>That's an example of just two viewport properties. The following syntax shows all of the
+supported viewport properties and the general types of values accepted by each one:</p>
+
+<pre>
+<meta name="viewport"
+ content="
+ <b>height</b> = [<em>pixel_value</em> | device-height] ,
+ <b>width</b> = [<em>pixel_value</em> | device-width ] ,
+ <b>initial-scale</b> = <em>float_value</em> ,
+ <b>minimum-scale</b> = <em>float_value</em> ,
+ <b>maximum-scale</b> = <em>float_value</em> ,
+ <b>user-scalable</b> = [yes | no] ,
+ <b>target-densitydpi</b> = [<em>dpi_value</em> | device-dpi |
+ high-dpi | medium-dpi | low-dpi]
+ " />
+</pre>
+
+<p>The following sections discuss how to use each of these viewport properties and exactly what the
+accepted values are.</p>
+
+<div class="figure" style="width:300px">
+ <img src="{@docRoot}images/webapps/compare-default.png" alt="" height="300" />
+ <p class="img-caption"><strong>Figure 1.</strong> A web page with no viewport metadata and an
+image that's 320 pixels wide (the viewport is 800 pixels wide, by default).</p>
+</div>
+
+
+<div class="figure" style="width:300px">
+ <img src="{@docRoot}images/webapps/compare-width400.png" alt="" height="300" />
+ <p class="img-caption"><strong>Figure 2.</strong> A web page with viewport {@code width=400}
+(the image in the web page is 320 pixels wide).</p>
+</div>
+
+
+<h3 id="ViewportSize">Defining the viewport size</h3>
+
+<p>Viewport's {@code height} and {@code width} properties allow you to specify the size of the
+viewport (the number of pixels available to the web page before it goes off screen). By default, the
+Android Browser's minimum viewport width is 800 pixels, so if your web
+page specifies its size to be 320 pixels wide, then your page renders smaller than the visible
+screen (even if the physical screen is 320 pixels wide, because the viewport simulates a
+drawable area that's 800 pixels wide), as shown in figure 1. So, you should explicitly define the
+viewport {@code width} to match the width for which you have designed your web page.</p>
+
+<p class="note"><strong>Note:</strong> Width values that are greater than 10,000 are ignored and
+values less than (or equal to) 320 result in a value equal to the device-width. Height values that
+are greater then 10,000 or less than 200 are also ignored.</p>
+
+<p>For example, if your web page is designed to be exactly 320 pixels wide, then you might
+want to specify that for the viewport width:</p>
+
+<pre>
+<meta name="viewport" content="width=320" />
+</pre>
+
+<p>In this case, your web page exactly fits the screen width, because the web page width and
+viewport width are the same.</p>
+
+<p>To demonstrate how this property affects the size of
+your web page, figure 2 shows a web page that contains an image that's 320 pixels wide, but with the
+viewport width set to 400.</p>
+
+
+<p class="note"><strong>Note:</strong> If you set the viewport width to match your web page width
+and the device screen width does <em>not</em> match those dimensions, then the web page
+still fits the screen even if the device has a high or low-density screen, because the
+Android Browser scales web pages to match the perceived size on a medium-density
+screen, by default (as you can see in figure 2, when comparing the hdpi device to the mdpi device).
+Screen densities are discussed more in <a href="#ViewportDensity">Defining the viewport target
+density</a>.</p>
+
+
+<h4>Automatic sizing</h4>
+
+<p>As an alternative to specifying the viewport dimensions with exact pixels, you can set the
+viewport size to always match the dimensions of the device screen, by defining the
+viewport properties {@code height}
+and {@code width} with the values {@code device-height} and {@code device-width}, respectively. This
+is appropriate when you're developing a web application that has a fluid width (not fixed width),
+but you want it to appear as if it's fixed (to perfectly fit every screen as
+if the web page width is set to match each screen). For example:</p>
+
+<pre>
+<meta name="viewport" content="width=device-width" />
+</pre>
+
+<p>This results in the viewport width matching whatever the current screen width is, as shown in
+figure 3. It's important to notice that, this results in images being scaled to fit the screen
+when the current device does not match the <a href="#ViewportDensity">target
+density</a>, which is medium-density if you don't specify otherwise. As a result, the image
+displayed on the high-density device in figure 3 is scaled up in order to match the width
+of a screen with a medium-density screen.</p>
+
+<div class="figure" style="width:300px">
+ <img src="{@docRoot}images/webapps/compare-initialscale.png" alt="" height="300" />
+ <p class="img-caption"><strong>Figure 3.</strong> A web page with viewport {@code
+width=device-width} <em>or</em> {@code initial-scale=1.0}.</p>
+</div>
+
+<p class="note"><strong>Note:</strong> If you instead want {@code
+device-width} and {@code device-height} to match the physical screen pixels for every device,
+instead of scaling your web page to match the target density, then you must also include
+the {@code target-densitydpi} property with a value of {@code device-dpi}. This is discussed more in
+the section about <a href="#ViewportDensity">Defining the viewport density</a>. Otherwise, simply
+using {@code device-height} and {@code device-width} to define the viewport size makes your web page
+fit every device screen, but scaling occurs on your images in order to adjust for different screen
+densities.</p>
+
+
+
+<h3 id="ViewportScale">Defining the viewport scale</h3>
+
+<p>The scale of the viewport defines the level of zoom applied to the web page. Viewport
+properties allow you to specify the scale of your web page in the following ways:</p>
+<dl>
+ <dt>{@code initial-scale}</dt>
+ <dd>The initial scale of the page. The value is a float that indicates a multiplier for your web
+page size, relative to the screen size. For example, if you set the initial scale to "1.0" then the
+web page is displayed to match the resolution of the <a href="#ViewportDensity">target
+density</a> 1-to-1. If set to "2.0", then the page is enlarged (zoomed in) by a factor of 2.
+ <p>The default initial scale is calculated to fit the web page in the viewport size.
+Because the default viewport width is 800 pixels, if the device screen resolution is less than
+800 pixels wide, the initial scale is something less than 1.0, by default, in order to fit the
+800-pixel-wide page on the screen.</p></dd>
+
+ <dt>{@code minimum-scale}</dt>
+ <dd>The minimum scale to allow. The value is a float that indicates the minimum multiplier for
+your web page size, relative to the screen size. For example, if you set this to "1.0", then the
+page can't zoom out because the minimum size is 1-to-1 with the <a href="#ViewportDensity">target
+density</a>.</dd>
+
+ <dt>{@code maximum-scale}</dt>
+ <dd>The maximum scale to allow for the page. The value is a float that indicates the
+maximum multiplier for your web page size,
+relative to the screen size. For example, if you set this to "2.0", then the page can't
+zoom in more than 2 times the target size.</dd>
+
+ <dt>{@code user-scalable}</dt>
+ <dd>Whether the user can change the scale of the page at all (zoom in and out). Set to {@code yes}
+to allow scaling and {@code no} to disallow scaling. The default is {@code yes}. If you set
+this to {@code no}, then the {@code minimum-scale} and {@code maximum-scale} are ignored,
+because scaling is not possible.</dd>
+</dl>
+
+<p>All scale values must be within the range 0.01–10.</p>
+
+<p>For example:</p>
+
+<pre>
+<meta name="viewport" content="initial-scale=1.0" />
+</pre>
+
+<p>This metadata sets the initial scale to be full sized, relative to the viewport's target
+density.</p>
+
+
+
+
+<h3 id="ViewportDensity">Defining the viewport target density</h3>
+
+<p>The density of a device's screen is based on the screen resolution. There are three screen
+density categories supported by Android: low (ldpi), medium (mdpi), and high (mdpi). A screen
+with low density has fewer available pixels per inch, whereas a screen with high density has more
+pixels per inch (compared to a medium density screen). The Android Browser targets a medium density
+screen by default.</p>
+
+
+<div class="figure" style="width:300px">
+ <img src="{@docRoot}images/webapps/compare-initialscale-devicedpi.png" alt="" height="300" />
+ <p class="img-caption"><strong>Figure 4.</strong> A web page with viewport {@code
+width=device-width} and {@code target-densitydpi=device-dpi}.</p>
+</div>
+
+
+<p>Because the default target density is medium, when users have a device with a low or high density
+screen, the Android Browser scales web pages (effectively zooms the pages) so they display at a
+size that matches the perceived appearance on a medium density screen. Specifically, the Android
+Browser applies approximately 1.5x scaling to web pages on a high density screen
+(because its screen pixels are smaller) and approximately 0.75x scaling to pages on a low density
+screen (because its screen pixels are bigger).</p>
+
+<p>Due to this default scaling, figures 1, 2, and 3 show the example web page at the same physical
+size on both the high and medium density device (the high-density device shows the
+web page with a default scale factor that is 1.5 times larger than the actual pixel resolution, to
+match the target density). This can introduce some undesirable artifacts in your images.
+For example, although an image appears the same size on a medium and high-density device, the image
+on the high-density device appears more blurry, because the image is designed to be 320 pixels
+wide, but is drawn with 480 pixels.</p>
+
+<p>You can change the target screen density for your web page using the {@code target-densitydpi}
+viewport property. It accepts the following values:</p>
+
+<ul>
+<li><code>device-dpi</code> - Use the device's native dpi as the target dpi. Default scaling never
+occurs.</li>
+<li><code>high-dpi</code> - Use hdpi as the target dpi. Medium and low density screens scale down
+as appropriate.</li>
+<li><code>medium-dpi</code> - Use mdpi as the target dpi. High density screens scale up and low
+density screens scale down. This is the default target density.</li>
+<li><code>low-dpi</code> - Use ldpi as the target dpi. Medium and high density screens scale up
+as appropriate.</li>
+<li><em><code><value></code></em> - Specify a dpi value to use as the target dpi. Values must
+be within the range 70–400.</li>
+</ul></p>
+
+<p>For example, to prevent the Android Browser from scaling of your web page for different screen
+densities, set
+the {@code target-densitydpi} viewport property to {@code device-dpi}. When you do, the Android
+Browser does not scale the page and, instead, displays your web page to match the current screen
+density. In this case, you should also define the viewport width to match the device width, so your
+web page naturally fits the screen size. For example:</p>
+
+<pre>
+<meta name="viewport" content="target-densitydpi=device-dpi, width=device-width" />
+</pre>
+
+<p>Figure 4 shows a web page using these viewport settings—the high-density device
+now displays the page smaller because its physical pixels are smaller than those on the
+medium-density device, so no scaling occurs and the 320-pixel-wide image is drawn using exactly 320
+pixels on both screens. (This is how you should define your viewport if
+you want to customize your web page based on screen density and provide different image assets for
+different densities, <a href="#DensityCSS">with CSS</a> or
+<a href="#DensityJS">with JavaScript</a>.)</p>
+
+
+<h2 id="DensityCSS">Targetting Device Density with CSS</h2>
+
+<p>The Android Browser supports a CSS media feature that allows you to create styles for specific
+screen densities—the <code>-webkit-device-pixel-ratio</code> CSS media feature. The
+value you apply to this feature should be either
+"0.75", "1", or "1.5", to indicate that the styles are for devices with low density, medium density,
+or high density screens, respectively.</p>
+
+<p>For example, you can create separate stylesheets for each density:</p>
+
+<pre>
+<link rel="stylesheet" media="screen and (-webkit-device-pixel-ratio: 1.5)" href="hdpi.css" />
+<link rel="stylesheet" media="screen and (-webkit-device-pixel-ratio: 1.0)" href="mdpi.css" />
+<link rel="stylesheet" media="screen and (-webkit-device-pixel-ratio: 0.75)" href="ldpi.css" />
+</pre>
+
+
+<div class="figure" style="width:300px">
+ <img src="{@docRoot}images/webapps/compare-width-devicedpi-css.png" alt="" height="300" />
+ <p class="img-caption"><strong>Figure 5.</strong> A web page with CSS that's targetted to
+specific screen densities using the {@code -webkit-device-pixel-ratio} media feature. Notice
+that the hdpi device shows a different image that's applied in CSS.</p>
+</div>
+
+<p>Or, specify the different styles in one stylesheet:</p>
+
+<pre class="no-pretty-print">
+#header {
+ background:url(medium-density-image.png);
+}
+
+@media screen and (-webkit-device-pixel-ratio: 1.5) {
+ // CSS for high-density screens
+ #header {
+ background:url(high-density-image.png);
+ }
+}
+
+@media screen and (-webkit-device-pixel-ratio: 0.75) {
+ // CSS for low-density screens
+ #header {
+ background:url(low-density-image.png);
+ }
+}
+</pre>
+
+<p class="note"><strong>Note:</strong> The default style for {@code #header} applies the image
+designed for medium-density devices in order to support devices running a version of Android less
+than 2.0, which do not support the {@code -webkit-device-pixel-ratio} media feature.</p>
+
+<p>The types of styles you might want to adjust based on the screen density depend on how you've
+defined your viewport properties. To provide fully-customized styles that tailor your web page for
+each of the supported densities, you should set your viewport properties so the viewport width and
+density match the device. That is:</p>
+
+<pre>
+<meta name="viewport" content="target-densitydpi=device-dpi, width=device-width" />
+</pre>
+
+<p>This way, the Android Browser does not perform scaling on your web page and the viewport width
+matches the screen width exactly. On its own, these viewport properties create results shown in
+figure 4. However, by adding some custom CSS using the {@code -webkit-device-pixel-ratio} media
+feature, you can apply different styles. For example, figure 5 shows a web page with these viewport
+properties and also some CSS added that applies a high-resolution image for high-density
+screens.</p>
+
+
+
+<h2 id="DensityJS">Targetting Device Density with JavaScript</h2>
+
+<p>The Android Browser supports a DOM property that allows you to query the density of the current
+device—the <code>window.devicePixelRatio</code> DOM property. The value of this property
+specifies the scaling factor used for the current device. For example, if the value
+of <code>window.devicePixelRatio</code> is "1.0", then the device is considered a medium density
+device and no scaling is applied by default; if the value is "1.5", then the device is
+considered a high density device and the page is scaled 1.5x by default; if the value
+is "0.75", then the device is considered a low density device and the page is scaled
+0.75x by default. Of course, the scaling that the Android Browser applies is based on the web page's
+target density—as described in the section about <a href="#ViewportDensity">Defining the
+viewport target density</a>, the default target is medium-density, but you can change the
+target to affect how your web page is scaled for different screen densities.</p>
+
+<p>For example, here's how you can query the device density with JavaScript:</p>
+
+<pre>
+if (window.devicePixelRatio == 1.5) {
+ alert("This is a high-density screen");
+} else if (window.devicePixelRation == 0.75) {
+ alert("This is a low-density screen");
+}
+</pre>
+
+
+
+
+
+
+
diff --git a/docs/html/guide/webapps/webview.jd b/docs/html/guide/webapps/webview.jd
new file mode 100644
index 0000000..ed28f21
--- /dev/null
+++ b/docs/html/guide/webapps/webview.jd
@@ -0,0 +1,328 @@
+page.title=Building Web Apps in WebView
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+<h2>Quickview</h2>
+<ul>
+ <li>Use {@link android.webkit.WebView} to display web pages in your Android application
+layout</li>
+ <li>You can create interfaces from your JavaScript to your client-side Android code</li>
+</ul>
+
+<h2>In this document</h2>
+<ol>
+ <li><a href="#AddingWebView">Adding a WebView to Your Application</a></li>
+ <li><a href="#UsingJavaScript">Using JavaScript in WebView</a>
+ <ol>
+ <li><a href="#EnablingJavaScript">Enabling JavaScript</a></li>
+ <li><a href="#BindingJavaScript">Binding JavaScript code to Android code</a></li>
+ </ol>
+ </li>
+ <li><a href="#HandlingNavigation">Handling Page Navigation</a>
+ <ol>
+ <li><a href="#NavigatingHistory">Navigating web page history</a></li>
+ </ol>
+ </li>
+</ol>
+
+<h2>Key classes</h2>
+<ol>
+ <li>{@link android.webkit.WebView}</li>
+ <li>{@link android.webkit.WebSettings}</li>
+ <li>{@link android.webkit.WebViewClient}</li>
+</ol>
+
+<h2>Related tutorials</h2>
+<ol>
+ <li><a href="{@docRoot}resources/tutorials/views/hello-webview.html">Web View</a></li>
+</ol>
+
+</div>
+</div>
+
+<p>If you want to deliver a web application (or just a web page) as a part of a client application,
+you can do it using {@link android.webkit.WebView}. The {@link android.webkit.WebView} class is an
+extension of Android's {@link android.view.View} class that allows you to display web pages as a
+part of your activity layout. It does <em>not</em> include any features of a fully developed web
+browser, such as navigation controls or an address bar. All that {@link android.webkit.WebView}
+does, by default, is show a web page.</p>
+
+<p>A common scenario in which using {@link android.webkit.WebView} is helpful is when you want to
+provide information in your application that you might need to update, such as an end-user agreement
+or a user guide. Within your Android application, you can create an {@link android.app.Activity}
+that contains a {@link android.webkit.WebView}, then use that to display your document that's
+hosted online.</p>
+
+<p>Another scenario in which {@link android.webkit.WebView} can help is if your application provides
+data to the user that
+always requires an Internet connection to retrieve data, such as email. In this case, you might
+find that it's easier to build a {@link android.webkit.WebView} in your Android application that
+shows a web page with all
+the user data, rather than performing a network request, then parsing the data and rendering it in
+an Android layout. Instead, you can design a web page that's tailored for Android devices
+and then implement a {@link android.webkit.WebView} in your Android application that loads the web
+page.</p>
+
+<p>This document shows you how to get started with {@link android.webkit.WebView} and how to do some
+additional things, such as handle page navigation and bind JavaScript from your web page to
+client-side code in your Android application.</p>
+
+
+
+<h2 id="AddingWebView">Adding a WebView to Your Application</h2>
+
+<p>To add a {@link android.webkit.WebView} to your Application, simply include the {@code
+<WebView>} element in your activity layout. For example, here's a layout file in which the
+{@link android.webkit.WebView} fills the screen:</p>
+
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<WebView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/webview"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+/>
+</pre>
+
+<p>To load a web page in the {@link android.webkit.WebView}, use {@link
+android.webkit.WebView#loadUrl(String) loadUrl()}. For example:</p>
+
+<pre>
+WebView myWebView = (WebView) findViewById(R.id.webview);
+myWebView.loadUrl("http://www.example.com");
+</pre>
+
+<p>Before this will work, however, your application must have access to the Internet. To get
+Internet access, request the {@link android.Manifest.permission#INTERNET} permission in your
+manifest file. For example:</p>
+
+<pre>
+<manifest ... >
+ <uses-permission android:name="android.permission.INTERNET" />
+ ...
+</manifest>
+</pre>
+
+<p>That's all you need for a basic {@link android.webkit.WebView} that displays a web page.</p>
+
+
+
+
+<h2 id="UsingJavaScript">Using JavaScript in WebView</h2>
+
+<p>If the web page you plan to load in your {@link android.webkit.WebView} use JavaScript, you
+must enable JavaScript for your {@link android.webkit.WebView}. Once JavaScript is enabled, you can
+also create interfaces between your application code and your JavaScript code.</p>
+
+
+<h3 id="EnablingJavaScript">Enabling JavaScript</h3>
+
+<p>JavaScript is disabled in a {@link android.webkit.WebView} by default. You can enable it
+through the {@link
+android.webkit.WebSettings} attached to your {@link android.webkit.WebView}. You can retrieve {@link
+android.webkit.WebSettings} with {@link android.webkit.WebView#getSettings()}, then enable
+JavaScript with {@link android.webkit.WebSettings#setJavaScriptEnabled(boolean)
+setJavaScriptEnabled()}.</p>
+
+<p>For example:</p>
+
+<pre>
+WebView myWebView = (WebView) findViewById(R.id.webview);
+WebSettings webSettings = myWebView.getSettings();
+webSettings.setJavaScriptEnabled(true);
+</pre>
+
+<p>{@link android.webkit.WebSettings} provides access to a variety of other settings that you might
+find useful. For example, if you're developing a web application
+that's designed specifically for the {@link android.webkit.WebView} in your Android application,
+then you can define a
+custom user agent string with {@link android.webkit.WebSettings#setUserAgentString(String)
+setUserAgentString()}, then query the custom user agent in your web page to verify that the
+client requesting your web page is actually your Android application.</p>
+
+from your Android SDK {@code tools/} directory
+<h3 id="BindingJavaScript">Binding JavaScript code to Android code</h3>
+
+<p>When developing a web application that's designed specifically for the {@link
+android.webkit.WebView} in your Android
+application, you can create interfaces between your JavaScript code and client-side Android code.
+For example, your JavaScript code can call a method in your Android code to display a {@link
+android.app.Dialog}, instead of using JavaScript's {@code alert()} function.</p>
+
+<p>To bind a new interface between your JavaScript and Android code, call {@link
+android.webkit.WebView#addJavascriptInterface(Object,String) addJavascriptInterface()}, passing it
+a class instance to bind to your JavaScript and an interface name that your JavaScript can call to
+access the class.</p>
+
+<p>For example, you can include the following class in your Android application:</p>
+
+<pre>
+public class JavaScriptInterface {
+ Context mContext;
+
+ /** Instantiate the interface and set the context */
+ JavaScriptInterface(Context c) {
+ mContext = c;
+ }
+
+ /** Show a toast from the web page */
+ public void showToast(String toast) {
+ Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
+ }
+}
+</pre>
+
+<p>In this example, the {@code JavaScriptInterface} class allows the web page to create a {@link
+android.widget.Toast} message, using the {@code showToast()} method.</p>
+
+<p>You can bind this class to the JavaScript that runs in your {@link android.webkit.WebView} with
+{@link android.webkit.WebView#addJavascriptInterface(Object,String) addJavascriptInterface()} and
+name the interface {@code Android}. For example:</p>
+
+<pre>
+WebView webView = (WebView) findViewById(R.id.webview);
+webView.addJavascriptInterface(new JavaScriptInterface(this), "Android");
+</pre>
+
+<p>This creates an interface called {@code Android} for JavaScript running in the {@link
+android.webkit.WebView}. At this point, your web application has access to the {@code
+JavaScriptInterface} class. For example, here's some HTML and JavaScript that creates a toast
+message using the new interface when the user clicks a button:</p>
+
+<pre>
+<input type="button" value="Say hello" onClick="showAndroidToast('Hello Android!')" />
+
+<script type="text/javascript">
+ function showAndroidToast(toast) {
+ Android.showToast(toast);
+ }
+</script>
+</pre>
+
+<p>There's no need to initialize the {@code Android} interface from JavaScript. The {@link
+android.webkit.WebView} automatically makes it
+available to your web page. So, at the click of the button, the {@code showAndroidToast()}
+function uses the {@code Android} interface to call the {@code JavaScriptInterface.showToast()}
+method.</p>
+
+<p class="note"><strong>Note:</strong> The object that is bound to your JavaScript runs in
+another thread and not in the thread in which it was constructed.</p>
+
+<p class="caution"><strong>Caution:</strong> Using {@link
+android.webkit.WebView#addJavascriptInterface(Object,String) addJavascriptInterface()} allows
+JavaScript to control your Android application. This can be a very useful feature or a dangerous
+security issue. When the HTML in the {@link android.webkit.WebView} is untrustworthy (for example,
+part or all of the HTML
+is provided by an unknown person or process), then an attacker can include HTML that executes
+your client-side code and possibly any code of the attacker's choosing. As such, you should not use
+{@link android.webkit.WebView#addJavascriptInterface(Object,String) addJavascriptInterface()} unless
+you wrote all of the HTML and JavaScript that appears in your {@link android.webkit.WebView}. You
+should also not allow the user to
+navigate to other web pages that are not your own, within your {@link android.webkit.WebView}
+(instead, allow the user's
+default browser application to open foreign links—by default, the user's web browser
+opens all URL links, so be careful only if you handle page navigation as described in the
+following section).</p>
+
+
+
+
+<h2 id="HandlingNavigation">Handling Page Navigation</h2>
+
+<p>When the user clicks a link from a web page in your {@link android.webkit.WebView}, the default
+behavior is
+for Android to launch an application that handles URLs. Usually, the default web browser opens and
+loads the destination URL. However, you can override this behavior for your {@link
+android.webkit.WebView},
+so links open within your {@link android.webkit.WebView}. You can then allow the user to navigate
+backward and forward through their web page history that's maintained by your {@link
+android.webkit.WebView}.</p>
+
+<p>To open links clicked by the user, simply provide a {@link
+android.webkit.WebViewClient} for your {@link android.webkit.WebView}, using {@link
+android.webkit.WebView#setWebViewClient(WebViewClient) setWebViewClient()}. For example:</p>
+
+<pre>
+WebView myWebView = (WebView) findViewById(R.id.webview);
+myWebView.{@link android.webkit.WebView#setWebViewClient(WebViewClient) setWebViewClient}(new WebViewClient());
+</pre>
+
+<p>That's it. Now all links the user clicks load in your {@link android.webkit.WebView}.</p>
+
+<p>If you want more control over where a clicked link load, create your own {@link
+android.webkit.WebViewClient} that overrides the {@link
+android.webkit.WebViewClient#shouldOverrideUrlLoading(WebView,String)
+shouldOverrideUrlLoading()} method. For example:</p>
+
+<pre>
+private class MyWebViewClient extends WebViewClient {
+ @Override
+ public boolean {@link android.webkit.WebViewClient#shouldOverrideUrlLoading(WebView,String) shouldOverrideUrlLoading}(WebView view, String url) {
+ if (Uri.parse(url).getHost().equals("www.example.com")) {
+ // This is my web site, so do not override; let my WebView load the page
+ return false;
+ }
+ // Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ startActivity(intent);
+ return true;
+ }
+}
+</pre>
+
+<p>Then create an instance of this new {@link android.webkit.WebViewClient} for the {@link
+android.webkit.WebView}:</p>
+
+<pre>
+WebView myWebView = (WebView) findViewById(R.id.webview);
+myWebView.{@link android.webkit.WebView#setWebViewClient(WebViewClient) setWebViewClient}(new MyWebViewClient());
+</pre>
+
+<p>Now when the user clicks a link, the system calls
+{@link android.webkit.WebViewClient#shouldOverrideUrlLoading(WebView,String)
+shouldOverrideUrlLoading()}, which checks whether the URL host matches a specific domain (as defined
+above). If it does match, then the method returns false in order to <em>not</em> override the URL
+loading (it allows the {@link android.webkit.WebView} to load the URL as usual). If the URL host
+does not match, then an {@link android.content.Intent} is created to
+launch the default Activity for handling URLs (which resolves to the user's default web
+browser).</p>
+
+
+
+
+<h3 id="NavigatingHistory">Navigating web page history</h3>
+
+<p>When your {@link android.webkit.WebView} overrides URL loading, it automatically accumulates a
+history of visited web
+pages. You can navigate backward and forward through the history with {@link
+android.webkit.WebView#goBack()} and {@link android.webkit.WebView#goForward()}.</p>
+
+<p>For example, here's how your {@link android.app.Activity} can use the device BACK key to navigate
+backward:</p>
+
+<pre>
+@Override
+public boolean {@link android.app.Activity#onKeyDown(int,KeyEvent) onKeyDown}(int keyCode, KeyEvent event) {
+ // Check if the key event was the BACK key and if there's history
+ if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.{@link android.webkit.WebView#canGoBack() canGoBack}() {
+ myWebView.{@link android.webkit.WebView#goBack() goBack}();
+ return true;
+ }
+ // If it wasn't the BACK key or there's no web page history, bubble up to the default
+ // system behavior (probably exit the activity)
+ return super.onKeyDown(keyCode, event);
+}
+</pre>
+
+<p>The {@link android.webkit.WebView#canGoBack()} method returns
+true if there is actually web page history for the user to visit. Likewise, you can use {@link
+android.webkit.WebView#canGoForward()} to check whether there is a forward history. If you don't
+perform this check, then once the user reaches the end of the history, {@link
+android.webkit.WebView#goBack()} or {@link android.webkit.WebView#goForward()} does nothing.</p>
+
+
+
+
+
+
diff --git a/docs/html/images/webapps/compare-default.png b/docs/html/images/webapps/compare-default.png
new file mode 100644
index 0000000..9495a05
--- /dev/null
+++ b/docs/html/images/webapps/compare-default.png
Binary files differ
diff --git a/docs/html/images/webapps/compare-initialscale-devicedpi.png b/docs/html/images/webapps/compare-initialscale-devicedpi.png
new file mode 100644
index 0000000..6bb758a
--- /dev/null
+++ b/docs/html/images/webapps/compare-initialscale-devicedpi.png
Binary files differ
diff --git a/docs/html/images/webapps/compare-initialscale.png b/docs/html/images/webapps/compare-initialscale.png
new file mode 100644
index 0000000..2232d5b
--- /dev/null
+++ b/docs/html/images/webapps/compare-initialscale.png
Binary files differ
diff --git a/docs/html/images/webapps/compare-width-devicedpi-css.png b/docs/html/images/webapps/compare-width-devicedpi-css.png
new file mode 100644
index 0000000..bb4ab31
--- /dev/null
+++ b/docs/html/images/webapps/compare-width-devicedpi-css.png
Binary files differ
diff --git a/docs/html/images/webapps/compare-width400.png b/docs/html/images/webapps/compare-width400.png
new file mode 100644
index 0000000..669a234
--- /dev/null
+++ b/docs/html/images/webapps/compare-width400.png
Binary files differ
diff --git a/docs/html/sitemap.txt b/docs/html/sitemap.txt
index d5be8f1..7a0b8ac 100644
--- a/docs/html/sitemap.txt
+++ b/docs/html/sitemap.txt
@@ -164,6 +164,11 @@
http://developer.android.com/guide/practices/design/performance.html
http://developer.android.com/guide/practices/design/responsiveness.html
http://developer.android.com/guide/practices/design/seamlessness.html
+http://developer.android.com/guide/webapps/targetting.html
+http://developer.android.com/guide/webapps/webview.html
+http://developer.android.com/guide/webapps/debugging.html
+http://developer.android.com/guide/webapps/best-practices.html
+http://developer.android.com/guide/topics/admin/device-admin.html
http://developer.android.com/guide/appendix/api-levels.html
http://developer.android.com/guide/appendix/media-formats.html
http://developer.android.com/guide/appendix/g-app-intents.html
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 53543b3..ff28f3b 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -876,12 +876,19 @@
return OK;
}
+// static
+void AwesomePlayer::OnRTSPSeekDoneWrapper(void *cookie) {
+ static_cast<AwesomePlayer *>(cookie)->onRTSPSeekDone();
+}
+
+void AwesomePlayer::onRTSPSeekDone() {
+ notifyListener_l(MEDIA_SEEK_COMPLETE);
+ mSeekNotificationSent = true;
+}
+
status_t AwesomePlayer::seekTo_l(int64_t timeUs) {
if (mRTSPController != NULL) {
- mRTSPController->seek(timeUs);
-
- notifyListener_l(MEDIA_SEEK_COMPLETE);
- mSeekNotificationSent = true;
+ mRTSPController->seekAsync(timeUs, OnRTSPSeekDoneWrapper, this);
return OK;
}
diff --git a/media/libstagefright/include/ARTSPController.h b/media/libstagefright/include/ARTSPController.h
index c2f3090..ce7ffe5 100644
--- a/media/libstagefright/include/ARTSPController.h
+++ b/media/libstagefright/include/ARTSPController.h
@@ -33,7 +33,7 @@
status_t connect(const char *url);
void disconnect();
- void seek(int64_t timeUs);
+ void seekAsync(int64_t timeUs, void (*seekDoneCb)(void *), void *cookie);
virtual size_t countTracks();
virtual sp<MediaSource> getTrack(size_t index);
@@ -61,6 +61,7 @@
enum {
kWhatConnectDone = 'cdon',
kWhatDisconnectDone = 'ddon',
+ kWhatSeekDone = 'sdon',
};
enum State {
@@ -79,6 +80,10 @@
sp<MyHandler> mHandler;
sp<AHandlerReflector<ARTSPController> > mReflector;
+ void (*mSeekDoneCb)(void *);
+ void *mSeekDoneCookie;
+ int64_t mLastSeekCompletedTimeUs;
+
DISALLOW_EVIL_CONSTRUCTORS(ARTSPController);
};
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index bbf482d..079adca 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -251,6 +251,9 @@
static bool ContinuePreparation(void *cookie);
+ static void OnRTSPSeekDoneWrapper(void *cookie);
+ void onRTSPSeekDone();
+
AwesomePlayer(const AwesomePlayer &);
AwesomePlayer &operator=(const AwesomePlayer &);
};
diff --git a/media/libstagefright/rtsp/ARTSPController.cpp b/media/libstagefright/rtsp/ARTSPController.cpp
index 4c53639..a7563ff 100644
--- a/media/libstagefright/rtsp/ARTSPController.cpp
+++ b/media/libstagefright/rtsp/ARTSPController.cpp
@@ -27,7 +27,10 @@
ARTSPController::ARTSPController(const sp<ALooper> &looper)
: mState(DISCONNECTED),
- mLooper(looper) {
+ mLooper(looper),
+ mSeekDoneCb(NULL),
+ mSeekDoneCookie(NULL),
+ mLastSeekCompletedTimeUs(-1) {
mReflector = new AHandlerReflector<ARTSPController>(this);
looper->registerHandler(mReflector);
}
@@ -80,14 +83,31 @@
mHandler.clear();
}
-void ARTSPController::seek(int64_t timeUs) {
+void ARTSPController::seekAsync(
+ int64_t timeUs,
+ void (*seekDoneCb)(void *), void *cookie) {
Mutex::Autolock autoLock(mLock);
- if (mState != CONNECTED) {
+ CHECK(seekDoneCb != NULL);
+ CHECK(mSeekDoneCb == NULL);
+
+ // Ignore seek requests that are too soon after the previous one has
+ // completed, we don't want to swamp the server.
+
+ bool tooEarly =
+ mLastSeekCompletedTimeUs >= 0
+ && ALooper::GetNowUs() < mLastSeekCompletedTimeUs + 500000ll;
+
+ if (mState != CONNECTED || tooEarly) {
+ (*seekDoneCb)(cookie);
return;
}
- mHandler->seek(timeUs);
+ mSeekDoneCb = seekDoneCb;
+ mSeekDoneCookie = cookie;
+
+ sp<AMessage> msg = new AMessage(kWhatSeekDone, mReflector->id());
+ mHandler->seek(timeUs, msg);
}
size_t ARTSPController::countTracks() {
@@ -132,6 +152,19 @@
break;
}
+ case kWhatSeekDone:
+ {
+ LOGI("seek done");
+
+ mLastSeekCompletedTimeUs = ALooper::GetNowUs();
+
+ void (*seekDoneCb)(void *) = mSeekDoneCb;
+ mSeekDoneCb = NULL;
+
+ (*seekDoneCb)(mSeekDoneCookie);
+ break;
+ }
+
default:
TRESPASS();
break;
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 2c9cfd3..05dd61b 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -88,7 +88,8 @@
mCheckPending(false),
mCheckGeneration(0),
mTryTCPInterleaving(false),
- mReceivedFirstRTCPPacket(false) {
+ mReceivedFirstRTCPPacket(false),
+ mSeekable(false) {
mNetLooper->setName("rtsp net");
mNetLooper->start(false /* runOnCallingThread */,
false /* canCallJava */,
@@ -115,9 +116,10 @@
(new AMessage('abor', id()))->post();
}
- void seek(int64_t timeUs) {
+ void seek(int64_t timeUs, const sp<AMessage> &doneMsg) {
sp<AMessage> msg = new AMessage('seek', id());
msg->setInt64("time", timeUs);
+ msg->setMessage("doneMsg", doneMsg);
msg->post();
}
@@ -379,6 +381,7 @@
mFirstAccessUnitNTP = 0;
mNumAccessUnitsReceived = 0;
mReceivedFirstRTCPPacket = false;
+ mSeekable = false;
sp<AMessage> reply = new AMessage('tear', id());
@@ -551,7 +554,17 @@
case 'seek':
{
+ sp<AMessage> doneMsg;
+ CHECK(msg->findMessage("doneMsg", &doneMsg));
+
if (mSeekPending) {
+ doneMsg->post();
+ break;
+ }
+
+ if (!mSeekable) {
+ LOGW("This is a live stream, ignoring seek request.");
+ doneMsg->post();
break;
}
@@ -577,6 +590,7 @@
sp<AMessage> reply = new AMessage('see1', id());
reply->setInt64("time", timeUs);
+ reply->setMessage("doneMsg", doneMsg);
mConn->sendRequest(request.c_str(), reply);
break;
}
@@ -605,7 +619,11 @@
request.append("\r\n");
+ sp<AMessage> doneMsg;
+ CHECK(msg->findMessage("doneMsg", &doneMsg));
+
sp<AMessage> reply = new AMessage('see2', id());
+ reply->setMessage("doneMsg", doneMsg);
mConn->sendRequest(request.c_str(), reply);
break;
}
@@ -644,6 +662,11 @@
}
mSeekPending = false;
+
+ sp<AMessage> doneMsg;
+ CHECK(msg->findMessage("doneMsg", &doneMsg));
+
+ doneMsg->post();
break;
}
@@ -714,6 +737,8 @@
}
void parsePlayResponse(const sp<ARTSPResponse> &response) {
+ mSeekable = false;
+
ssize_t i = response->mHeaders.indexOfKey("range");
if (i < 0) {
// Server doesn't even tell use what range it is going to
@@ -777,6 +802,8 @@
++n;
}
+
+ mSeekable = true;
}
sp<APacketSource> getPacketSource(size_t index) {
@@ -808,6 +835,7 @@
int32_t mCheckGeneration;
bool mTryTCPInterleaving;
bool mReceivedFirstRTCPPacket;
+ bool mSeekable;
struct TrackInfo {
AString mURL;
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index ce0e79f..37b4c1d 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -3931,17 +3931,22 @@
allowed = false;
} else if (bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE
|| bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
- allowed = (checkSignaturesLP(bp.packageSetting.signatures.mSignatures, pkg.mSignatures)
+ allowed = (checkSignaturesLP(
+ bp.packageSetting.signatures.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH)
|| (checkSignaturesLP(mPlatformPackage.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH);
- if (bp.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
+ if (!allowed && bp.protectionLevel
+ == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
if (isSystemApp(pkg)) {
// For updated system applications, the signatureOrSystem permission
// is granted only if it had been defined by the original application.
if (isUpdatedSystemApp(pkg)) {
- PackageSetting sysPs = mSettings.getDisabledSystemPkg(pkg.packageName);
- if(sysPs.grantedPermissions.contains(perm)) {
+ PackageSetting sysPs = mSettings.getDisabledSystemPkg(
+ pkg.packageName);
+ final GrantedPermissions origGp = sysPs.sharedUser != null
+ ? sysPs.sharedUser : sysPs;
+ if (origGp.grantedPermissions.contains(perm)) {
allowed = true;
} else {
allowed = false;
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 3d95bf0..a63b3d8 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -2163,12 +2163,12 @@
if (DBG) throw new IllegalArgumentException("lockMode=" + lockMode);
return false;
}
- if (ws != null) {
- enforceWakeSourcePermission(Binder.getCallingUid(), Binder.getCallingPid());
- }
if (ws != null && ws.size() == 0) {
ws = null;
}
+ if (ws != null) {
+ enforceWakeSourcePermission(Binder.getCallingUid(), Binder.getCallingPid());
+ }
if (ws == null) {
ws = new WorkSource(Binder.getCallingUid());
}
@@ -2227,17 +2227,18 @@
++mScanLocksAcquired;
break;
}
+
+ // Be aggressive about adding new locks into the accounted state...
+ // we want to over-report rather than under-report.
+ reportStartWorkSource();
+
+ updateWifiState();
+ return true;
} catch (RemoteException e) {
+ return false;
} finally {
Binder.restoreCallingIdentity(ident);
}
-
- // Be aggressive about adding new locks into the accounted state...
- // we want to over-report rather than under-report.
- reportStartWorkSource();
-
- updateWifiState();
- return true;
}
public void updateWifiLockWorkSource(IBinder lock, WorkSource ws) {
@@ -2283,9 +2284,9 @@
hadLock = (wifiLock != null);
- if (hadLock) {
- long ident = Binder.clearCallingIdentity();
- try {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ if (hadLock) {
noteAcquireWifiLock(wifiLock);
switch(wifiLock.mMode) {
case WifiManager.WIFI_MODE_FULL:
@@ -2298,13 +2299,16 @@
++mScanLocksReleased;
break;
}
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(ident);
}
+
+ // TODO - should this only happen if you hadLock?
+ updateWifiState();
+
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
- // TODO - should this only happen if you hadLock?
- updateWifiState();
+
return hadLock;
}