Merge "DO NOT MERGE Do not kill sockets going default->hipri" into gingerbread
diff --git a/Android.mk b/Android.mk
index b1f43f8..9f92637 100644
--- a/Android.mk
+++ b/Android.mk
@@ -375,6 +375,8 @@
# (see development/build/sdk.atree)
web_docs_sample_code_flags := \
-hdf android.hasSamples 1 \
+ -samplecode $(sample_dir)/AccessibilityService \
+ resources/samples/AccessibilityService "Accessibility Service" \
-samplecode $(sample_dir)/ApiDemos \
resources/samples/ApiDemos "API Demos" \
-samplecode $(sample_dir)/BackupRestore \
diff --git a/api/9.xml b/api/9.xml
index f18623f..46f087f 100644
--- a/api/9.xml
+++ b/api/9.xml
@@ -379555,7 +379555,7 @@
type="java.lang.String"
transient="false"
volatile="false"
- value=""100-Continue""
+ value=""100-continue""
static="true"
final="true"
deprecated="not deprecated"
diff --git a/api/current.xml b/api/current.xml
index 65a8a8e..61f5e16 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -96284,6 +96284,17 @@
visibility="public"
>
</field>
+<field name="ERROR_CANNOT_RESUME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1008"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="ERROR_DEVICE_NOT_FOUND"
type="int"
transient="false"
@@ -121330,6 +121341,16 @@
visibility="public"
>
</field>
+<field name="SERIAL"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="TAGS"
type="java.lang.String"
transient="false"
@@ -403590,7 +403611,7 @@
type="java.lang.String"
transient="false"
volatile="false"
- value=""100-Continue""
+ value=""100-continue""
static="true"
final="true"
deprecated="not deprecated"
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index e99d4b4..2870c50 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2844,6 +2844,7 @@
boolean returnValue;
boolean hasListeners;
+ boolean changesMade = false;
List<String> keysModified = null;
Set<OnSharedPreferenceChangeListener> listeners = null;
@@ -2857,17 +2858,31 @@
synchronized (this) {
if (mClear) {
- mMap.clear();
+ if (!mMap.isEmpty()) {
+ changesMade = true;
+ mMap.clear();
+ }
mClear = false;
}
for (Entry<String, Object> e : mModified.entrySet()) {
String k = e.getKey();
Object v = e.getValue();
- if (v == this) {
- mMap.remove(k);
+ if (v == this) { // magic value for a removal mutation
+ if (mMap.containsKey(k)) {
+ mMap.remove(k);
+ changesMade = true;
+ }
} else {
- mMap.put(k, v);
+ boolean isSame = false;
+ if (mMap.containsKey(k)) {
+ Object existingValue = mMap.get(k);
+ isSame = existingValue != null && existingValue.equals(v);
+ }
+ if (!isSame) {
+ mMap.put(k, v);
+ changesMade = true;
+ }
}
if (hasListeners) {
@@ -2878,7 +2893,7 @@
mModified.clear();
}
- returnValue = writeFileLocked();
+ returnValue = writeFileLocked(changesMade);
}
if (hasListeners) {
@@ -2923,9 +2938,16 @@
return str;
}
- private boolean writeFileLocked() {
+ private boolean writeFileLocked(boolean changesMade) {
// Rename the current file so it may be used as a backup during the next read
if (mFile.exists()) {
+ if (!changesMade) {
+ // If the file already exists, but no changes were
+ // made to the underlying map, it's wasteful to
+ // re-write the file. Return as if we wrote it
+ // out.
+ return true;
+ }
if (!mBackupFile.exists()) {
if (!mFile.renameTo(mBackupFile)) {
Log.e(TAG, "Couldn't rename file " + mFile
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index f72de67..a0abc6c 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -1138,9 +1138,9 @@
*/
public static final String SCENE_MODE_BARCODE = "barcode";
- // Values for focus mode settings.
/**
- * Auto-focus mode.
+ * Auto-focus mode. Applications should call {@link
+ * #autoFocus(AutoFocusCallback)} to start the focus in this mode.
*/
public static final String FOCUS_MODE_AUTO = "auto";
@@ -1149,6 +1149,12 @@
* {@link #autoFocus(AutoFocusCallback)} in this mode.
*/
public static final String FOCUS_MODE_INFINITY = "infinity";
+
+ /**
+ * Macro (close-up) focus mode. Applications should call
+ * {@link #autoFocus(AutoFocusCallback)} to start the focus in this
+ * mode.
+ */
public static final String FOCUS_MODE_MACRO = "macro";
/**
@@ -1170,7 +1176,9 @@
* Continuous auto focus mode. The camera continuously tries to focus.
* This is ideal for shooting video or shooting photo of moving object.
* Auto focus starts when the parameter is set. Applications should not
- * call {@link #autoFocus(AutoFocusCallback)} in this mode.
+ * call {@link #autoFocus(AutoFocusCallback)} in this mode. To stop
+ * continuous focus, applications should change the focus mode to other
+ * modes.
*/
public static final String FOCUS_MODE_CONTINUOUS = "continuous";
@@ -1948,6 +1956,7 @@
* @see #FOCUS_MODE_INFINITY
* @see #FOCUS_MODE_MACRO
* @see #FOCUS_MODE_FIXED
+ * @see #FOCUS_MODE_EDOF
* @see #FOCUS_MODE_CONTINUOUS
*/
public String getFocusMode() {
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index f6d237a..d2c3eaa 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -1525,7 +1525,22 @@
* Typically the atmospheric pressure is read from a
* {@link Sensor#TYPE_PRESSURE} sensor. The pressure at sea level must be
* known, usually it can be retrieved from airport databases in the
- * vicinity.
+ * vicinity. If unknown, you can use {@link #PRESSURE_STANDARD_ATMOSPHERE}
+ * as an approximation, but absolute altitudes won't be accurate.
+ * </p>
+ * <p>
+ * To calculate altitude differences, you must calculate the difference
+ * between the altitudes at both points. If you don't know the altitude
+ * as sea level, you can use {@link #PRESSURE_STANDARD_ATMOSPHERE} instead,
+ * which will give good results considering the range of pressure typically
+ * involved.
+ * </p>
+ * <p>
+ * <code><ul>
+ * float altitude_difference =
+ * getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, pressure_at_point2)
+ * - getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, pressure_at_point1);
+ * </ul></code>
* </p>
*
* @param p0 pressure at sea level
diff --git a/core/java/android/net/DownloadManager.java b/core/java/android/net/DownloadManager.java
index e69c324..cafe0f9 100644
--- a/core/java/android/net/DownloadManager.java
+++ b/core/java/android/net/DownloadManager.java
@@ -51,14 +51,14 @@
public final static String COLUMN_ID = "id";
/**
- * The client-supplied title for this download. This will be displayed in system notifications,
- * if enabled.
+ * The client-supplied title for this download. This will be displayed in system notifications.
+ * Defaults to the empty string.
*/
public final static String COLUMN_TITLE = "title";
/**
* The client-supplied description of this download. This will be displayed in system
- * notifications, if enabled.
+ * notifications. Defaults to the empty string.
*/
public final static String COLUMN_DESCRIPTION = "description";
@@ -68,22 +68,24 @@
public final static String COLUMN_URI = "uri";
/**
- * Internet Media Type of the downloaded file. This will be filled in based on the server's
- * response once the download has started.
+ * Internet Media Type of the downloaded file. If no value is provided upon creation, this will
+ * initially be null and will be filled in based on the server's response once the download has
+ * started.
*
* @see <a href="http://www.ietf.org/rfc/rfc1590.txt">RFC 1590, defining Media Types</a>
*/
public final static String COLUMN_MEDIA_TYPE = "media_type";
/**
- * Total size of the download in bytes. This will be filled in once the download starts.
+ * Total size of the download in bytes. This will initially be -1 and will be filled in once
+ * the download starts.
*/
public final static String COLUMN_TOTAL_SIZE_BYTES = "total_size";
/**
* Uri where downloaded file will be stored. If a destination is supplied by client, that URI
- * will be used here. Otherwise, the value will be filled in with a generated URI once the
- * download has started.
+ * will be used here. Otherwise, the value will initially be null and will be filled in with a
+ * generated URI once the download has started.
*/
public final static String COLUMN_LOCAL_URI = "local_uri";
@@ -185,6 +187,12 @@
public final static int ERROR_DEVICE_NOT_FOUND = 1007;
/**
+ * Value of {@link #COLUMN_ERROR_CODE} when some possibly transient error occurred but we can't
+ * resume the download.
+ */
+ public final static int ERROR_CANNOT_RESUME = 1008;
+
+ /**
* Broadcast intent action sent by the download manager when a download completes.
*/
public final static String ACTION_DOWNLOAD_COMPLETE = "android.intent.action.DOWNLOAD_COMPLETE";
@@ -682,8 +690,13 @@
if (column.equals(COLUMN_MEDIA_TYPE)) {
return getUnderlyingString(Downloads.COLUMN_MIME_TYPE);
}
+
assert column.equals(COLUMN_LOCAL_URI);
- return Uri.fromFile(new File(getUnderlyingString(Downloads._DATA))).toString();
+ String localUri = getUnderlyingString(Downloads._DATA);
+ if (localUri == null) {
+ return null;
+ }
+ return Uri.fromFile(new File(localUri)).toString();
}
private long translateLong(String column) {
@@ -715,7 +728,8 @@
if (translateStatus(status) != STATUS_FAILED) {
return 0; // arbitrary value when status is not an error
}
- if ((400 <= status && status < 490) || (500 <= status && status < 600)) {
+ if ((400 <= status && status < Downloads.Impl.MIN_ARTIFICIAL_ERROR_STATUS)
+ || (500 <= status && status < 600)) {
// HTTP status code
return status;
}
@@ -740,6 +754,9 @@
case Downloads.STATUS_DEVICE_NOT_FOUND_ERROR:
return ERROR_DEVICE_NOT_FOUND;
+ case Downloads.Impl.STATUS_CANNOT_RESUME:
+ return ERROR_CANNOT_RESUME;
+
default:
return ERROR_UNKNOWN;
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index a0a3bdf..95f217f 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -23,6 +23,7 @@
import android.util.Log;
import android.util.Printer;
import android.util.SparseArray;
+import android.util.TimeUtils;
/**
* A class providing access to battery usage statistics, including information on
@@ -1576,8 +1577,10 @@
Uid.Proc.ExcessiveWake ew = ps.getExcessiveWake(e);
if (ew != null) {
pw.print(prefix); pw.print(" * Killed for wake lock use: ");
- pw.print(ew.usedTime); pw.print("ms over ");
- pw.print(ew.overTime); pw.print("ms (");
+ TimeUtils.formatDuration(ew.usedTime, pw);
+ pw.print(" over ");
+ TimeUtils.formatDuration(ew.overTime, pw);
+ pw.print(" (");
pw.print((ew.usedTime*100)/ew.overTime);
pw.println("%)");
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index fdd3573..8925bd0 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -62,6 +62,9 @@
/** The name of the hardware (from the kernel command line or /proc). */
public static final String HARDWARE = getString("ro.hardware");
+ /** A hardware serial number, if available. Alphanumeric only, case-insensitive. */
+ public static final String SERIAL = getString("ro.serialno");
+
/** Various version strings. */
public static class VERSION {
/**
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index a9d7342..d360140 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -192,10 +192,11 @@
pw.println(prefix + "mQueue=" + ((mQueue != null) ? mQueue : "(null"));
if (mQueue != null) {
synchronized (mQueue) {
+ long now = SystemClock.uptimeMillis();
Message msg = mQueue.mMessages;
int n = 0;
while (msg != null) {
- pw.println(prefix + " Message " + n + ": " + msg);
+ pw.println(prefix + " Message " + n + ": " + msg.toString(now));
n++;
msg = msg.next;
}
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index 476da1d..49b72fe 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -19,6 +19,7 @@
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.TimeUtils;
/**
*
@@ -366,13 +367,17 @@
}
public String toString() {
+ return toString(SystemClock.uptimeMillis());
+ }
+
+ String toString(long now) {
StringBuilder b = new StringBuilder();
b.append("{ what=");
b.append(what);
b.append(" when=");
- b.append(when);
+ TimeUtils.formatDuration(when-now, b);
if (arg1 != 0) {
b.append(" arg1=");
diff --git a/core/java/android/pim/vcard/VCardBuilder.java b/core/java/android/pim/vcard/VCardBuilder.java
index 1da6d7a..d634672 100644
--- a/core/java/android/pim/vcard/VCardBuilder.java
+++ b/core/java/android/pim/vcard/VCardBuilder.java
@@ -1463,6 +1463,9 @@
parameterList.add(VCardConstants.PARAM_TYPE_VOICE);
} else if (VCardUtils.isMobilePhoneLabel(label)) {
parameterList.add(VCardConstants.PARAM_TYPE_CELL);
+ } else if (mIsV30) {
+ // This label is appropriately encoded in appendTypeParameters.
+ parameterList.add(label);
} else {
final String upperLabel = label.toUpperCase();
if (VCardUtils.isValidInV21ButUnknownToContactsPhoteType(upperLabel)) {
@@ -1741,21 +1744,30 @@
// which would be recommended way in vcard 3.0 though not valid in vCard 2.1.
boolean first = true;
for (final String typeValue : types) {
- // Note: vCard 3.0 specifies the different type of acceptable type Strings, but
- // we don't emit that kind of vCard 3.0 specific type since there should be
- // high probabilyty in which external importers cannot understand them.
- //
- // e.g. TYPE="\u578B\u306B\u3087" (vCard 3.0 allows non-Ascii characters if they
- // are quoted.)
- if (!VCardUtils.isV21Word(typeValue)) {
- continue;
+ if (VCardConfig.isV30(mVCardType)) {
+ // Note: vCard 3.0 specifies the different type of acceptable type Strings, but
+ // we don't emit that kind of vCard 3.0 specific type since there should be
+ // high probabilyty in which external importers cannot understand them.
+ //
+ // e.g. TYPE="\u578B\u306B\u3087" (vCard 3.0 allows non-Ascii characters if they
+ // are quoted.)
+ if (first) {
+ first = false;
+ } else {
+ mBuilder.append(VCARD_PARAM_SEPARATOR);
+ }
+ appendTypeParameter(VCardUtils.toStringAvailableAsV30ParameValue(typeValue));
+ } else { // vCard 2.1
+ if (!VCardUtils.isV21Word(typeValue)) {
+ continue;
+ }
+ if (first) {
+ first = false;
+ } else {
+ mBuilder.append(VCARD_PARAM_SEPARATOR);
+ }
+ appendTypeParameter(typeValue);
}
- if (first) {
- first = false;
- } else {
- mBuilder.append(VCARD_PARAM_SEPARATOR);
- }
- appendTypeParameter(typeValue);
}
}
diff --git a/core/java/android/pim/vcard/VCardEntryConstructor.java b/core/java/android/pim/vcard/VCardEntryConstructor.java
index 290ca2b..ae4ec29 100644
--- a/core/java/android/pim/vcard/VCardEntryConstructor.java
+++ b/core/java/android/pim/vcard/VCardEntryConstructor.java
@@ -157,11 +157,15 @@
mParamType = type;
}
+ @Override
public void propertyParamValue(String value) {
if (mParamType == null) {
// From vCard 2.1 specification. vCard 3.0 formally does not allow this case.
mParamType = "TYPE";
}
+ if (!VCardUtils.containsOnlyAlphaDigitHyphen(value)) {
+ value = encodeString(value, mCharsetForDecodedBytes);
+ }
mCurrentProperty.addParameter(mParamType, value);
mParamType = null;
}
diff --git a/core/java/android/pim/vcard/VCardUtils.java b/core/java/android/pim/vcard/VCardUtils.java
index 11b112b..f972799 100644
--- a/core/java/android/pim/vcard/VCardUtils.java
+++ b/core/java/android/pim/vcard/VCardUtils.java
@@ -16,10 +16,10 @@
package android.pim.vcard;
import android.content.ContentProviderOperation;
-import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.CommonDataKinds.Im;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.Data;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
@@ -477,6 +477,43 @@
return true;
}
+ /**
+ * <P>
+ * Returns String available as parameter value in vCard 3.0.
+ * </P>
+ * <P>
+ * RFC 2426 requires vCard composer to quote parameter values when it contains
+ * semi-colon, for example (See RFC 2426 for more information).
+ * This method checks whether the given String can be used without quotes.
+ * </P>
+ * <P>
+ * Note: We remove DQUOTE silently for now.
+ * </P>
+ */
+ public static String toStringAvailableAsV30ParameValue(String value) {
+ if (TextUtils.isEmpty(value)) {
+ value = "";
+ }
+ final int asciiFirst = 0x20;
+ final int asciiLast = 0x7E; // included
+ final StringBuilder builder = new StringBuilder();
+ final int length = value.length();
+ boolean needQuote = false;
+ for (int i = 0; i < length; i = value.offsetByCodePoints(i, 1)) {
+ final int codePoint = value.codePointAt(i);
+ if (codePoint < asciiFirst || codePoint == '"') {
+ // CTL characters and DQUOTE are never accepted. Remove them.
+ continue;
+ }
+ builder.appendCodePoint(codePoint);
+ if (codePoint == ':' || codePoint == ',' || codePoint == ' ') {
+ needQuote = true;
+ }
+ }
+ final String result = builder.toString();
+ return ((needQuote || result.isEmpty()) ? ('"' + result + '"') : result);
+ }
+
public static String toHalfWidthString(final String orgString) {
if (TextUtils.isEmpty(orgString)) {
return null;
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index c9b5512..6bf0d5b 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -1060,6 +1060,16 @@
public static final int STATUS_PRECONDITION_FAILED = 412;
/**
+ * The lowest-valued error status that is not an actual HTTP status code.
+ */
+ public static final int MIN_ARTIFICIAL_ERROR_STATUS = 489;
+
+ /**
+ * Some possibly transient error occurred, but we can't resume the download.
+ */
+ public static final int STATUS_CANNOT_RESUME = 489;
+
+ /**
* This download was canceled
*/
public static final int STATUS_CANCELED = 490;
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 0fc70d5..b01a71d 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -24,6 +24,7 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.io.PrintWriter;
import java.util.TimeZone;
import java.util.Date;
@@ -130,4 +131,128 @@
public static String getTimeZoneDatabaseVersion() {
return ZoneInfoDB.getVersion();
}
+
+ private static final int SECONDS_PER_MINUTE = 60;
+ private static final int SECONDS_PER_HOUR = 60 * 60;
+ private static final int SECONDS_PER_DAY = 24 * 60 * 60;
+
+ /** @hide Just for debugging; not internationalized. */
+ public static void formatDuration(long duration, StringBuilder builder) {
+ if (duration == 0) {
+ builder.append("0");
+ return;
+ }
+ if (duration > 0) {
+ builder.append("+");
+ } else {
+ builder.append("-");
+ duration = -duration;
+ }
+
+ int millis = (int)(duration%1000);
+ int seconds = (int) Math.floor(duration / 1000);
+ int days = 0, hours = 0, minutes = 0;
+
+ if (seconds > SECONDS_PER_DAY) {
+ days = seconds / SECONDS_PER_DAY;
+ seconds -= days * SECONDS_PER_DAY;
+ }
+ if (seconds > SECONDS_PER_HOUR) {
+ hours = seconds / SECONDS_PER_HOUR;
+ seconds -= hours * SECONDS_PER_HOUR;
+ }
+ if (seconds > SECONDS_PER_MINUTE) {
+ minutes = seconds / SECONDS_PER_MINUTE;
+ seconds -= minutes * SECONDS_PER_MINUTE;
+ }
+
+ boolean doall = false;
+ if (days > 0) {
+ builder.append(days);
+ builder.append('d');
+ doall = true;
+ }
+ if (doall || hours > 0) {
+ builder.append(hours);
+ builder.append('h');
+ doall = true;
+ }
+ if (doall || minutes > 0) {
+ builder.append(minutes);
+ builder.append('m');
+ doall = true;
+ }
+ if (doall || seconds > 0) {
+ builder.append(seconds);
+ builder.append('s');
+ doall = true;
+ }
+ builder.append(millis);
+ builder.append("ms");
+ }
+
+ /** @hide Just for debugging; not internationalized. */
+ public static void formatDuration(long duration, PrintWriter pw) {
+ if (duration == 0) {
+ pw.print("0");
+ return;
+ }
+ if (duration > 0) {
+ pw.print("+");
+ } else {
+ pw.print("-");
+ duration = -duration;
+ }
+
+ int millis = (int)(duration%1000);
+ int seconds = (int) Math.floor(duration / 1000);
+ int days = 0, hours = 0, minutes = 0;
+
+ if (seconds > SECONDS_PER_DAY) {
+ days = seconds / SECONDS_PER_DAY;
+ seconds -= days * SECONDS_PER_DAY;
+ }
+ if (seconds > SECONDS_PER_HOUR) {
+ hours = seconds / SECONDS_PER_HOUR;
+ seconds -= hours * SECONDS_PER_HOUR;
+ }
+ if (seconds > SECONDS_PER_MINUTE) {
+ minutes = seconds / SECONDS_PER_MINUTE;
+ seconds -= minutes * SECONDS_PER_MINUTE;
+ }
+
+ boolean doall = false;
+ if (days > 0) {
+ pw.print(days);
+ pw.print('d');
+ doall = true;
+ }
+ if (doall || hours > 0) {
+ pw.print(hours);
+ pw.print('h');
+ doall = true;
+ }
+ if (doall || minutes > 0) {
+ pw.print(minutes);
+ pw.print('m');
+ doall = true;
+ }
+ if (doall || seconds > 0) {
+ pw.print(seconds);
+ pw.print('s');
+ doall = true;
+ }
+ pw.print(millis);
+ pw.print("ms");
+ }
+
+
+ /** @hide Just for debugging; not internationalized. */
+ public static void formatDuration(long time, long now, PrintWriter pw) {
+ if (time == 0) {
+ pw.print("--");
+ return;
+ }
+ formatDuration(time-now, pw);
+ }
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index fabe5c8..34d7935 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -27,7 +27,8 @@
/**
- * Use the WindowManager interface to create a Display object.
+ * Use {@link android.view.WindowManager#getDefaultDisplay()
+ * WindowManager.getDefaultDisplay()} to create a Display object.
* Display gives you access to some information about a particular display
* connected to the device.
*/
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 2ca08ea..402443c 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -990,7 +990,8 @@
}
}) : 0;
long durationDraw =
- (root || (view.mPrivateFlags & View.DRAWN) != 0) ? profileViewOperation(view,
+ (root || !view.willNotDraw() || (view.mPrivateFlags & View.DRAWN) != 0) ? profileViewOperation(
+ view,
new ViewOperation<Object>() {
public Object[] pre() {
final DisplayMetrics metrics =
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
index ce5959d..e1c5564 100644
--- a/core/java/com/android/internal/app/PlatLogoActivity.java
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -18,17 +18,31 @@
import android.app.Activity;
import android.os.Bundle;
+import android.view.MotionEvent;
import android.widget.ImageView;
+import android.widget.Toast;
public class PlatLogoActivity extends Activity {
+ Toast mToast;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mToast = Toast.makeText(this, "Zombie art by Jack Larson", Toast.LENGTH_SHORT);
+
ImageView content = new ImageView(this);
content.setImageResource(com.android.internal.R.drawable.platlogo);
content.setScaleType(ImageView.ScaleType.FIT_CENTER);
setContentView(content);
}
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_UP) {
+ mToast.show();
+ }
+ return super.dispatchTouchEvent(ev);
+ }
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 2f26135..2a5b944 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -27,7 +27,6 @@
import android.os.Parcelable;
import android.os.Process;
import android.os.SystemClock;
-import android.os.BatteryStats.Uid.Proc.ExcessiveWake;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
@@ -1241,25 +1240,31 @@
int mWakeLockNesting;
public void noteStartWakeLocked(int uid, int pid, String name, int type) {
- if (mWakeLockNesting == 0) {
- mHistoryCur.states |= HistoryItem.STATE_WAKE_LOCK_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ if (type == WAKE_TYPE_PARTIAL) {
+ // Only care about partial wake locks, since full wake locks
+ // will be canceled when the user puts the screen to sleep.
+ if (mWakeLockNesting == 0) {
+ mHistoryCur.states |= HistoryItem.STATE_WAKE_LOCK_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ }
+ mWakeLockNesting++;
}
- mWakeLockNesting++;
if (uid >= 0) {
getUidStatsLocked(uid).noteStartWakeLocked(pid, name, type);
}
}
public void noteStopWakeLocked(int uid, int pid, String name, int type) {
- mWakeLockNesting--;
- if (mWakeLockNesting == 0) {
- mHistoryCur.states &= ~HistoryItem.STATE_WAKE_LOCK_FLAG;
- if (DEBUG_HISTORY) Slog.v(TAG, "Stop wake lock to: "
- + Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ if (type == WAKE_TYPE_PARTIAL) {
+ mWakeLockNesting--;
+ if (mWakeLockNesting == 0) {
+ mHistoryCur.states &= ~HistoryItem.STATE_WAKE_LOCK_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Stop wake lock to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(SystemClock.elapsedRealtime());
+ }
}
if (uid >= 0) {
getUidStatsLocked(uid).noteStopWakeLocked(pid, name, type);
@@ -1353,7 +1358,7 @@
// Fake a wake lock, so we consider the device waked as long
// as the screen is on.
- noteStartWakeLocked(-1, -1, "dummy", 0);
+ noteStartWakeLocked(-1, -1, "dummy", WAKE_TYPE_PARTIAL);
}
}
@@ -1369,7 +1374,7 @@
mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(this);
}
- noteStopWakeLocked(-1, -1, "dummy", 0);
+ noteStopWakeLocked(-1, -1, "dummy", WAKE_TYPE_PARTIAL);
}
}
@@ -3463,7 +3468,7 @@
if (t != null) {
t.startRunningLocked(BatteryStatsImpl.this);
}
- if (pid >= 0) {
+ if (pid >= 0 && type == WAKE_TYPE_PARTIAL) {
Pid p = getPidStatsLocked(pid);
p.mWakeStart = SystemClock.elapsedRealtime();
}
@@ -3474,7 +3479,7 @@
if (t != null) {
t.stopRunningLocked(BatteryStatsImpl.this);
}
- if (pid >= 0) {
+ if (pid >= 0 && type == WAKE_TYPE_PARTIAL) {
Pid p = mPids.get(pid);
if (p != null) {
p.mWakeSum += SystemClock.elapsedRealtime() - p.mWakeStart;
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index efdc399..42135c5 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -78,6 +78,7 @@
android_util_Process.cpp \
android_util_StringBlock.cpp \
android_util_XmlBlock.cpp \
+ android/graphics/AutoDecodeCancel.cpp \
android/graphics/Bitmap.cpp \
android/graphics/BitmapFactory.cpp \
android/graphics/Camera.cpp \
@@ -101,6 +102,7 @@
android_graphics_PixelFormat.cpp \
android/graphics/Picture.cpp \
android/graphics/PorterDuff.cpp \
+ android/graphics/LargeBitmap.cpp \
android/graphics/Rasterizer.cpp \
android/graphics/Region.cpp \
android/graphics/Shader.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 62ca2ef..407d2e7 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -53,6 +53,7 @@
extern int register_android_os_Process(JNIEnv* env);
extern int register_android_graphics_Bitmap(JNIEnv*);
extern int register_android_graphics_BitmapFactory(JNIEnv*);
+extern int register_android_graphics_LargeBitmap(JNIEnv*);
extern int register_android_graphics_Camera(JNIEnv* env);
extern int register_android_graphics_Graphics(JNIEnv* env);
extern int register_android_graphics_Interpolator(JNIEnv* env);
@@ -1264,6 +1265,7 @@
REG_JNI(register_android_graphics_Bitmap),
REG_JNI(register_android_graphics_BitmapFactory),
+ REG_JNI(register_android_graphics_LargeBitmap),
REG_JNI(register_android_graphics_Camera),
REG_JNI(register_android_graphics_Canvas),
REG_JNI(register_android_graphics_ColorFilter),
diff --git a/core/jni/android/graphics/AutoDecodeCancel.cpp b/core/jni/android/graphics/AutoDecodeCancel.cpp
new file mode 100644
index 0000000..f0739ea
--- /dev/null
+++ b/core/jni/android/graphics/AutoDecodeCancel.cpp
@@ -0,0 +1,100 @@
+#include "AutoDecodeCancel.h"
+
+static SkMutex gAutoDecoderCancelMutex;
+static AutoDecoderCancel* gAutoDecoderCancel;
+#ifdef SK_DEBUG
+static int gAutoDecoderCancelCount;
+#endif
+
+AutoDecoderCancel::AutoDecoderCancel(jobject joptions,
+ SkImageDecoder* decoder) {
+ fJOptions = joptions;
+ fDecoder = decoder;
+
+ if (NULL != joptions) {
+ SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
+
+ // Add us as the head of the list
+ fPrev = NULL;
+ fNext = gAutoDecoderCancel;
+ if (gAutoDecoderCancel) {
+ gAutoDecoderCancel->fPrev = this;
+ }
+ gAutoDecoderCancel = this;
+
+ SkDEBUGCODE(gAutoDecoderCancelCount += 1;)
+ Validate();
+ }
+}
+
+AutoDecoderCancel::~AutoDecoderCancel() {
+ if (NULL != fJOptions) {
+ SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
+
+ // take us out of the dllist
+ AutoDecoderCancel* prev = fPrev;
+ AutoDecoderCancel* next = fNext;
+
+ if (prev) {
+ SkASSERT(prev->fNext == this);
+ prev->fNext = next;
+ } else {
+ SkASSERT(gAutoDecoderCancel == this);
+ gAutoDecoderCancel = next;
+ }
+ if (next) {
+ SkASSERT(next->fPrev == this);
+ next->fPrev = prev;
+ }
+
+ SkDEBUGCODE(gAutoDecoderCancelCount -= 1;)
+ Validate();
+ }
+}
+
+bool AutoDecoderCancel::RequestCancel(jobject joptions) {
+ SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
+
+ Validate();
+
+ AutoDecoderCancel* pair = gAutoDecoderCancel;
+ while (pair != NULL) {
+ if (pair->fJOptions == joptions) {
+ pair->fDecoder->cancelDecode();
+ return true;
+ }
+ pair = pair->fNext;
+ }
+ return false;
+}
+
+#ifdef SK_DEBUG
+// can only call this inside a lock on gAutoDecoderCancelMutex
+void AutoDecoderCancel::Validate() {
+ const int gCount = gAutoDecoderCancelCount;
+
+ if (gCount == 0) {
+ SkASSERT(gAutoDecoderCancel == NULL);
+ } else {
+ SkASSERT(gCount > 0);
+
+ AutoDecoderCancel* curr = gAutoDecoderCancel;
+ SkASSERT(curr);
+ SkASSERT(curr->fPrev == NULL);
+
+ int count = 0;
+ while (curr) {
+ count += 1;
+ SkASSERT(count <= gCount);
+ if (curr->fPrev) {
+ SkASSERT(curr->fPrev->fNext == curr);
+ }
+ if (curr->fNext) {
+ SkASSERT(curr->fNext->fPrev == curr);
+ }
+ curr = curr->fNext;
+ }
+ SkASSERT(count == gCount);
+ }
+}
+#endif
diff --git a/core/jni/android/graphics/AutoDecodeCancel.h b/core/jni/android/graphics/AutoDecodeCancel.h
new file mode 100644
index 0000000..37b86f9
--- /dev/null
+++ b/core/jni/android/graphics/AutoDecodeCancel.h
@@ -0,0 +1,27 @@
+#ifndef AutoDecodeCancel_DEFINED
+#define AutoDecodeCancel_DEFINED
+
+#include <jni.h>
+#include "SkImageDecoder.h"
+
+class AutoDecoderCancel {
+public:
+ AutoDecoderCancel(jobject options, SkImageDecoder* decoder);
+ ~AutoDecoderCancel();
+
+ static bool RequestCancel(jobject options);
+
+private:
+ AutoDecoderCancel* fNext;
+ AutoDecoderCancel* fPrev;
+ jobject fJOptions; // java options object
+ SkImageDecoder* fDecoder;
+
+#ifdef SK_DEBUG
+ static void Validate();
+#else
+ static void Validate() {}
+#endif
+};
+
+#endif
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index b41bad0..21b2e3b 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -1,14 +1,15 @@
#define LOG_TAG "BitmapFactory"
+#include "BitmapFactory.h"
#include "SkImageDecoder.h"
#include "SkImageRef_ashmem.h"
#include "SkImageRef_GlobalPool.h"
#include "SkPixelRef.h"
#include "SkStream.h"
-#include "GraphicsJNI.h"
#include "SkTemplates.h"
#include "SkUtils.h"
#include "CreateJavaOutputStreamAdaptor.h"
+#include "AutoDecodeCancel.h"
#include <android_runtime/AndroidRuntime.h>
#include <utils/Asset.h>
@@ -16,18 +17,18 @@
#include <netinet/in.h>
#include <sys/mman.h>
-static jclass gOptions_class;
-static jfieldID gOptions_justBoundsFieldID;
-static jfieldID gOptions_sampleSizeFieldID;
-static jfieldID gOptions_configFieldID;
-static jfieldID gOptions_ditherFieldID;
-static jfieldID gOptions_purgeableFieldID;
-static jfieldID gOptions_shareableFieldID;
-static jfieldID gOptions_nativeAllocFieldID;
-static jfieldID gOptions_widthFieldID;
-static jfieldID gOptions_heightFieldID;
-static jfieldID gOptions_mimeFieldID;
-static jfieldID gOptions_mCancelID;
+jclass gOptions_class;
+jfieldID gOptions_justBoundsFieldID;
+jfieldID gOptions_sampleSizeFieldID;
+jfieldID gOptions_configFieldID;
+jfieldID gOptions_ditherFieldID;
+jfieldID gOptions_purgeableFieldID;
+jfieldID gOptions_shareableFieldID;
+jfieldID gOptions_nativeAllocFieldID;
+jfieldID gOptions_widthFieldID;
+jfieldID gOptions_heightFieldID;
+jfieldID gOptions_mimeFieldID;
+jfieldID gOptions_mCancelID;
static jclass gFileDescriptor_class;
static jfieldID gFileDescriptor_descriptor;
@@ -38,129 +39,6 @@
#define TRACE_BITMAP(code)
#endif
-///////////////////////////////////////////////////////////////////////////////
-
-class AutoDecoderCancel {
-public:
- AutoDecoderCancel(jobject options, SkImageDecoder* decoder);
- ~AutoDecoderCancel();
-
- static bool RequestCancel(jobject options);
-
-private:
- AutoDecoderCancel* fNext;
- AutoDecoderCancel* fPrev;
- jobject fJOptions; // java options object
- SkImageDecoder* fDecoder;
-
-#ifdef SK_DEBUG
- static void Validate();
-#else
- static void Validate() {}
-#endif
-};
-
-static SkMutex gAutoDecoderCancelMutex;
-static AutoDecoderCancel* gAutoDecoderCancel;
-#ifdef SK_DEBUG
- static int gAutoDecoderCancelCount;
-#endif
-
-AutoDecoderCancel::AutoDecoderCancel(jobject joptions,
- SkImageDecoder* decoder) {
- fJOptions = joptions;
- fDecoder = decoder;
-
- if (NULL != joptions) {
- SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
-
- // Add us as the head of the list
- fPrev = NULL;
- fNext = gAutoDecoderCancel;
- if (gAutoDecoderCancel) {
- gAutoDecoderCancel->fPrev = this;
- }
- gAutoDecoderCancel = this;
-
- SkDEBUGCODE(gAutoDecoderCancelCount += 1;)
- Validate();
- }
-}
-
-AutoDecoderCancel::~AutoDecoderCancel() {
- if (NULL != fJOptions) {
- SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
-
- // take us out of the dllist
- AutoDecoderCancel* prev = fPrev;
- AutoDecoderCancel* next = fNext;
-
- if (prev) {
- SkASSERT(prev->fNext == this);
- prev->fNext = next;
- } else {
- SkASSERT(gAutoDecoderCancel == this);
- gAutoDecoderCancel = next;
- }
- if (next) {
- SkASSERT(next->fPrev == this);
- next->fPrev = prev;
- }
-
- SkDEBUGCODE(gAutoDecoderCancelCount -= 1;)
- Validate();
- }
-}
-
-bool AutoDecoderCancel::RequestCancel(jobject joptions) {
- SkAutoMutexAcquire ac(gAutoDecoderCancelMutex);
-
- Validate();
-
- AutoDecoderCancel* pair = gAutoDecoderCancel;
- while (pair != NULL) {
- if (pair->fJOptions == joptions) {
- pair->fDecoder->cancelDecode();
- return true;
- }
- pair = pair->fNext;
- }
- return false;
-}
-
-#ifdef SK_DEBUG
-// can only call this inside a lock on gAutoDecoderCancelMutex
-void AutoDecoderCancel::Validate() {
- const int gCount = gAutoDecoderCancelCount;
-
- if (gCount == 0) {
- SkASSERT(gAutoDecoderCancel == NULL);
- } else {
- SkASSERT(gCount > 0);
-
- AutoDecoderCancel* curr = gAutoDecoderCancel;
- SkASSERT(curr);
- SkASSERT(curr->fPrev == NULL);
-
- int count = 0;
- while (curr) {
- count += 1;
- SkASSERT(count <= gCount);
- if (curr->fPrev) {
- SkASSERT(curr->fPrev->fNext == curr);
- }
- if (curr->fNext) {
- SkASSERT(curr->fNext->fPrev == curr);
- }
- curr = curr->fNext;
- }
- SkASSERT(count == gCount);
- }
-}
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-
using namespace android;
class NinePatchPeeker : public SkImageDecoder::Peeker {
@@ -279,7 +157,7 @@
return ((int32_t)isValid - 1) | value;
}
-static jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
+jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format) {
static const struct {
SkImageDecoder::Format fFormat;
const char* fMimeType;
@@ -477,7 +355,7 @@
jobject padding,
jobject options) { // BitmapFactory$Options
jobject bitmap = NULL;
- SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage);
+ SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 0);
if (stream) {
// for now we don't allow purgeable with java inputstreams
@@ -682,6 +560,107 @@
}
}
+static jobject doBuildTileIndex(JNIEnv* env, SkStream* stream, bool isShareable) {
+ SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
+ int width, height;
+ if (NULL == decoder) {
+ doThrowIOE(env, "Image format not supported");
+ return nullObjectReturn("SkImageDecoder::Factory returned null");
+ }
+
+ JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env, true);
+ decoder->setAllocator(javaAllocator);
+ JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env);
+ decoder->setReporter(javaMemoryReporter);
+ javaAllocator->unref();
+ javaMemoryReporter->unref();
+
+ if (!decoder->buildTileIndex(stream, &width, &height, isShareable)) {
+ char msg[1024];
+ snprintf(msg, 1023, "Image failed to decode using %s decoder", decoder->getFormatName());
+ doThrowIOE(env, msg);
+ return nullObjectReturn("decoder->buildTileIndex returned false");
+ }
+
+ SkLargeBitmap *bm = new SkLargeBitmap(decoder, width, height);
+
+ return GraphicsJNI::createLargeBitmap(env, bm);
+}
+
+static jobject nativeCreateLargeBitmapFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
+ int offset, int length, jboolean isShareable) {
+ AutoJavaByteArray ar(env, byteArray);
+ SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, false);
+ SkAutoUnref aur(stream);
+ if (isShareable) {
+ aur.detach();
+ }
+ return doBuildTileIndex(env, stream, isShareable);
+}
+
+static jobject nativeCreateLargeBitmapFromFileDescriptor(JNIEnv* env, jobject clazz,
+ jobject fileDescriptor, jboolean isShareable) {
+ NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
+
+ jint descriptor = env->GetIntField(fileDescriptor,
+ gFileDescriptor_descriptor);
+ bool weOwnTheFD = false;
+
+ if (isShareable) {
+ int newFD = ::dup(descriptor);
+ if (-1 != newFD) {
+ weOwnTheFD = true;
+ descriptor = newFD;
+ }
+ }
+
+ SkFDStream* stream = new SkFDStream(descriptor, weOwnTheFD);
+ SkAutoUnref aur(stream);
+ if (!stream->isValid()) {
+ return NULL;
+ }
+
+ if (isShareable) {
+ aur.detach();
+ }
+
+ /* Restore our offset when we leave, so we can be called more than once
+ with the same descriptor. This is only required if we didn't dup the
+ file descriptor, but it is OK to do it all the time.
+ */
+ AutoFDSeek as(descriptor);
+
+ return doBuildTileIndex(env, stream, isShareable);
+}
+
+static jobject nativeCreateLargeBitmapFromStream(JNIEnv* env, jobject clazz,
+ jobject is, // InputStream
+ jbyteArray storage, // byte[]
+ jboolean isShareable) {
+ jobject largeBitmap = NULL;
+ SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 1024);
+
+ if (stream) {
+ // for now we don't allow shareable with java inputstreams
+ largeBitmap = doBuildTileIndex(env, stream, false);
+ stream->unref();
+ }
+ return largeBitmap;
+}
+
+static jobject nativeCreateLargeBitmapFromAsset(JNIEnv* env, jobject clazz,
+ jint native_asset, // Asset
+ jboolean isShareable) {
+ SkStream* stream;
+ Asset* asset = reinterpret_cast<Asset*>(native_asset);
+ stream = new AssetStreamAdaptor(asset);
+ SkAutoUnref aur(stream);
+ if (isShareable) {
+ aur.detach();
+ }
+ return doBuildTileIndex(env, stream, isShareable);
+}
+
///////////////////////////////////////////////////////////////////////////////
static JNINativeMethod gMethods[] = {
@@ -711,6 +690,26 @@
},
{ "nativeSetDefaultConfig", "(I)V", (void*)nativeSetDefaultConfig },
+
+ { "nativeCreateLargeBitmap",
+ "([BIIZ)Landroid/graphics/LargeBitmap;",
+ (void*)nativeCreateLargeBitmapFromByteArray
+ },
+
+ { "nativeCreateLargeBitmap",
+ "(Ljava/io/InputStream;[BZ)Landroid/graphics/LargeBitmap;",
+ (void*)nativeCreateLargeBitmapFromStream
+ },
+
+ { "nativeCreateLargeBitmap",
+ "(Ljava/io/FileDescriptor;Z)Landroid/graphics/LargeBitmap;",
+ (void*)nativeCreateLargeBitmapFromFileDescriptor
+ },
+
+ { "nativeCreateLargeBitmap",
+ "(IZ)Landroid/graphics/LargeBitmap;",
+ (void*)nativeCreateLargeBitmapFromAsset
+ },
};
static JNINativeMethod gOptionsMethods[] = {
diff --git a/core/jni/android/graphics/BitmapFactory.h b/core/jni/android/graphics/BitmapFactory.h
new file mode 100644
index 0000000..f868434
--- /dev/null
+++ b/core/jni/android/graphics/BitmapFactory.h
@@ -0,0 +1,21 @@
+#ifndef BitmapFactory_DEFINE
+#define BitmapFactory_DEFINE
+
+#include "GraphicsJNI.h"
+
+extern jclass gOptions_class;
+extern jfieldID gOptions_justBoundsFieldID;
+extern jfieldID gOptions_sampleSizeFieldID;
+extern jfieldID gOptions_configFieldID;
+extern jfieldID gOptions_ditherFieldID;
+extern jfieldID gOptions_purgeableFieldID;
+extern jfieldID gOptions_shareableFieldID;
+extern jfieldID gOptions_nativeAllocFieldID;
+extern jfieldID gOptions_widthFieldID;
+extern jfieldID gOptions_heightFieldID;
+extern jfieldID gOptions_mimeFieldID;
+extern jfieldID gOptions_mCancelID;
+
+jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format);
+
+#endif
diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
index 007757f..137acc6 100644
--- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
+++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
@@ -5,6 +5,7 @@
static jclass gInputStream_Clazz;
static jmethodID gInputStream_resetMethodID;
+static jmethodID gInputStream_markMethodID;
static jmethodID gInputStream_availableMethodID;
static jmethodID gInputStream_readMethodID;
static jmethodID gInputStream_skipMethodID;
@@ -143,7 +144,7 @@
};
SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
- jbyteArray storage) {
+ jbyteArray storage, int markSize) {
static bool gInited;
if (!gInited) {
@@ -153,6 +154,8 @@
gInputStream_resetMethodID = env->GetMethodID(gInputStream_Clazz,
"reset", "()V");
+ gInputStream_markMethodID = env->GetMethodID(gInputStream_Clazz,
+ "mark", "(I)V");
gInputStream_availableMethodID = env->GetMethodID(gInputStream_Clazz,
"available", "()I");
gInputStream_readMethodID = env->GetMethodID(gInputStream_Clazz,
@@ -161,6 +164,7 @@
"skip", "(J)J");
RETURN_NULL_IF_NULL(gInputStream_resetMethodID);
+ RETURN_NULL_IF_NULL(gInputStream_markMethodID);
RETURN_NULL_IF_NULL(gInputStream_availableMethodID);
RETURN_NULL_IF_NULL(gInputStream_availableMethodID);
RETURN_NULL_IF_NULL(gInputStream_skipMethodID);
@@ -168,6 +172,10 @@
gInited = true;
}
+ if (markSize) {
+ env->CallVoidMethod(stream, gInputStream_markMethodID, markSize);
+ }
+
return new JavaInputStreamAdaptor(env, stream, storage);
}
diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h
index cf21dde..c34c96a 100644
--- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h
+++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h
@@ -6,7 +6,7 @@
#include "SkStream.h"
SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream,
- jbyteArray storage);
+ jbyteArray storage, int markSize = 0);
SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream,
jbyteArray storage);
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 5659ba2..204bb74 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -46,6 +46,10 @@
doThrow(env, "java/lang/OutOfMemoryError", msg);
}
+void doThrowIOE(JNIEnv* env, const char* msg) {
+ doThrow(env, "java/lang/IOException", msg);
+}
+
bool GraphicsJNI::hasException(JNIEnv *env) {
if (env->ExceptionCheck() != 0) {
LOGE("*** Uncaught exception returned from Java call!\n");
@@ -165,6 +169,9 @@
static jclass gBitmapConfig_class;
static jfieldID gBitmapConfig_nativeInstanceID;
+static jclass gLargeBitmap_class;
+static jmethodID gLargeBitmap_constructorMethodID;
+
static jclass gCanvas_class;
static jfieldID gCanvas_nativeInstanceID;
@@ -370,6 +377,23 @@
}
return obj;
}
+jobject GraphicsJNI::createLargeBitmap(JNIEnv* env, SkLargeBitmap* bitmap)
+{
+ SkASSERT(bitmap != NULL);
+
+ jobject obj = env->AllocObject(gLargeBitmap_class);
+ if (hasException(env)) {
+ obj = NULL;
+ return obj;
+ }
+ if (obj) {
+ env->CallVoidMethod(obj, gLargeBitmap_constructorMethodID, (jint)bitmap);
+ if (hasException(env)) {
+ obj = NULL;
+ }
+ }
+ return obj;
+}
jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region)
{
@@ -502,6 +526,35 @@
////////////////////////////////////////////////////////////////////////////////
+JavaMemoryUsageReporter::JavaMemoryUsageReporter(JNIEnv* env)
+ : fEnv(env), fTotalSize(0) {}
+
+JavaMemoryUsageReporter::~JavaMemoryUsageReporter() {
+ jlong jtotalSize = fTotalSize;
+ fEnv->CallVoidMethod(gVMRuntime_singleton,
+ gVMRuntime_trackExternalFreeMethodID,
+ jtotalSize);
+}
+
+bool JavaMemoryUsageReporter::reportMemory(size_t memorySize) {
+ jlong jsize = memorySize; // the VM wants longs for the size
+ bool r = fEnv->CallBooleanMethod(gVMRuntime_singleton,
+ gVMRuntime_trackExternalAllocationMethodID,
+ jsize);
+ if (GraphicsJNI::hasException(fEnv)) {
+ return false;
+ }
+ if (!r) {
+ LOGE("VM won't let us allocate %zd bytes\n", memorySize);
+ doThrowOOME(fEnv, "bitmap size exceeds VM budget");
+ return false;
+ }
+ fTotalSize += memorySize;
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
static jclass make_globalref(JNIEnv* env, const char classname[])
{
jclass c = env->FindClass(classname);
@@ -547,6 +600,9 @@
gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>",
"(IZ[BI)V");
+ gLargeBitmap_class = make_globalref(env, "android/graphics/LargeBitmap");
+ gLargeBitmap_constructorMethodID = env->GetMethodID(gLargeBitmap_class, "<init>", "(I)V");
+
gBitmapConfig_class = make_globalref(env, "android/graphics/Bitmap$Config");
gBitmapConfig_nativeInstanceID = getFieldIDCheck(env, gBitmapConfig_class,
"nativeInt", "I");
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index fe24b05..8d6528b 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -4,6 +4,8 @@
#include "SkPoint.h"
#include "SkRect.h"
#include "SkBitmap.h"
+#include "../images/SkLargeBitmap.h"
+#include "../images/SkImageDecoder.h"
#include <jni.h>
class SkCanvas;
@@ -54,6 +56,8 @@
static jobject createRegion(JNIEnv* env, SkRegion* region);
+ static jobject createLargeBitmap(JNIEnv* env, SkLargeBitmap* bitmap);
+
/** Set a pixelref for the bitmap (needs setConfig to already be called)
Returns true on success. If it returns false, then it failed, and the
appropriate exception will have been raised.
@@ -80,6 +84,18 @@
bool fReportSizeToVM;
};
+class JavaMemoryUsageReporter : public SkVMMemoryReporter {
+public:
+ JavaMemoryUsageReporter(JNIEnv* env);
+ virtual ~JavaMemoryUsageReporter();
+ // overrides
+ virtual bool reportMemory(size_t memorySize);
+
+private:
+ JNIEnv* fEnv;
+ size_t fTotalSize;
+};
+
enum JNIAccess {
kRO_JNIAccess,
kRW_JNIAccess
@@ -156,6 +172,7 @@
void doThrowRE(JNIEnv* env, const char* msg = NULL); // Runtime
void doThrowISE(JNIEnv* env, const char* msg = NULL); // Illegal State
void doThrowOOME(JNIEnv* env, const char* msg = NULL); // Out of memory
+void doThrowIOE(JNIEnv* env, const char* msg = NULL); // IO Exception
#define NPE_CHECK_RETURN_ZERO(env, object) \
do { if (NULL == (object)) { doThrowNPE(env); return 0; } } while (0)
diff --git a/core/jni/android/graphics/LargeBitmap.cpp b/core/jni/android/graphics/LargeBitmap.cpp
new file mode 100644
index 0000000..4cf5dfa
--- /dev/null
+++ b/core/jni/android/graphics/LargeBitmap.cpp
@@ -0,0 +1,138 @@
+#define LOG_TAG "LargeBitmap"
+
+#include "SkBitmap.h"
+#include "SkImageEncoder.h"
+#include "SkColorPriv.h"
+#include "GraphicsJNI.h"
+#include "SkDither.h"
+#include "SkUnPreMultiply.h"
+#include "SkUtils.h"
+#include "SkTemplates.h"
+#include "SkPixelRef.h"
+#include "BitmapFactory.h"
+#include "AutoDecodeCancel.h"
+#include "SkLargeBitmap.h"
+
+#include <binder/Parcel.h>
+#include "android_util_Binder.h"
+#include "android_nio_utils.h"
+#include "CreateJavaOutputStreamAdaptor.h"
+
+#include <jni.h>
+
+#if 0
+ #define TRACE_BITMAP(code) code
+#else
+ #define TRACE_BITMAP(code)
+#endif
+
+static jobject nullObjectReturn(const char msg[]) {
+ if (msg) {
+ SkDebugf("--- %s\n", msg);
+ }
+ return NULL;
+}
+
+/*
+ * nine patch not supported
+ *
+ * purgeable not supported
+ * reportSizeToVM not supported
+ */
+static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkLargeBitmap *bm,
+ int start_x, int start_y, int width, int height, jobject options) {
+ SkImageDecoder *decoder = bm->getDecoder();
+ int sampleSize = 1;
+ SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
+ bool doDither = true;
+
+ if (NULL != options) {
+ sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
+ // initialize these, in case we fail later on
+ env->SetIntField(options, gOptions_widthFieldID, -1);
+ env->SetIntField(options, gOptions_heightFieldID, -1);
+ env->SetObjectField(options, gOptions_mimeFieldID, 0);
+
+ jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
+ prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
+ doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
+ }
+
+ decoder->setDitherImage(doDither);
+ SkBitmap* bitmap = new SkBitmap;
+ SkAutoTDelete<SkBitmap> adb(bitmap);
+ AutoDecoderCancel adc(options, decoder);
+
+ // To fix the race condition in case "requestCancelDecode"
+ // happens earlier than AutoDecoderCancel object is added
+ // to the gAutoDecoderCancelMutex linked list.
+ if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
+ return nullObjectReturn("gOptions_mCancelID");;
+ }
+
+ SkIRect region;
+ region.fLeft = start_x;
+ region.fTop = start_y;
+ region.fRight = start_x + width;
+ region.fBottom = start_y + height;
+
+ if (!bm->decodeRegion(bitmap, region, prefConfig, sampleSize)) {
+ return nullObjectReturn("decoder->decodeRegion returned false");
+ }
+
+ // update options (if any)
+ if (NULL != options) {
+ env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
+ env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
+ // TODO: set the mimeType field with the data from the codec.
+ // but how to reuse a set of strings, rather than allocating new one
+ // each time?
+ env->SetObjectField(options, gOptions_mimeFieldID,
+ getMimeTypeString(env, decoder->getFormat()));
+ }
+
+ // detach bitmap from its autotdeleter, since we want to own it now
+ adb.detach();
+
+ SkPixelRef* pr;
+ pr = bitmap->pixelRef();
+ // promise we will never change our pixels (great for sharing and pictures)
+ pr->setImmutable();
+ // now create the java bitmap
+ return GraphicsJNI::createBitmap(env, bitmap, false, NULL);
+}
+
+static int nativeGetHeight(JNIEnv* env, jobject, SkLargeBitmap *bm) {
+ return bm->getHeight();
+}
+
+static int nativeGetWidth(JNIEnv* env, jobject, SkLargeBitmap *bm) {
+ return bm->getWidth();
+}
+
+static void nativeClean(JNIEnv* env, jobject, SkLargeBitmap *bm) {
+ delete bm;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include <android_runtime/AndroidRuntime.h>
+
+static JNINativeMethod gLargeBitmapMethods[] = {
+ { "nativeDecodeRegion",
+ "(IIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
+ (void*)nativeDecodeRegion},
+ { "nativeGetHeight", "(I)I", (void*)nativeGetHeight},
+ { "nativeGetWidth", "(I)I", (void*)nativeGetWidth},
+ { "nativeClean", "(I)V", (void*)nativeClean},
+};
+
+#define kClassPathName "android/graphics/LargeBitmap"
+
+int register_android_graphics_LargeBitmap(JNIEnv* env);
+int register_android_graphics_LargeBitmap(JNIEnv* env)
+{
+ return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
+ gLargeBitmapMethods, SK_ARRAY_COUNT(gLargeBitmapMethods));
+}
+
diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp
index 7392442..903283e 100644
--- a/core/jni/android_net_wifi_Wifi.cpp
+++ b/core/jni/android_net_wifi_Wifi.cpp
@@ -493,6 +493,15 @@
return doBooleanCommand("BLACKLIST clear", "OK");
}
+static jboolean android_net_wifi_setSuspendOptimizationsCommand(JNIEnv* env, jobject clazz, jboolean enabled)
+{
+ char cmdstr[25];
+
+ snprintf(cmdstr, sizeof(cmdstr), "DRIVER SETSUSPEND %d", enabled ? 0 : 1);
+ return doBooleanCommand(cmdstr, "OK");
+}
+
+
static jboolean android_net_wifi_doDhcpRequest(JNIEnv* env, jobject clazz, jobject info)
{
jint ipaddr, gateway, mask, dns1, dns2, server, lease;
@@ -571,6 +580,7 @@
{ "setScanResultHandlingCommand", "(I)Z", (void*) android_net_wifi_setScanResultHandlingCommand },
{ "addToBlacklistCommand", "(Ljava/lang/String;)Z", (void*) android_net_wifi_addToBlacklistCommand },
{ "clearBlacklistCommand", "()Z", (void*) android_net_wifi_clearBlacklistCommand },
+ { "setSuspendOptimizationsCommand", "(Z)Z", (void*) android_net_wifi_setSuspendOptimizationsCommand},
{ "doDhcpRequest", "(Landroid/net/DhcpInfo;)Z", (void*) android_net_wifi_doDhcpRequest },
{ "getDhcpError", "()Ljava/lang/String;", (void*) android_net_wifi_getDhcpError },
diff --git a/core/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp
index 556d367..42f35d1 100644
--- a/core/jni/android_view_InputQueue.cpp
+++ b/core/jni/android_view_InputQueue.cpp
@@ -76,10 +76,14 @@
STATUS_ZOMBIE
};
- Connection(const sp<InputChannel>& inputChannel, const sp<PollLoop>& pollLoop);
+ Connection(uint16_t id,
+ const sp<InputChannel>& inputChannel, const sp<PollLoop>& pollLoop);
inline const char* getInputChannelName() const { return inputChannel->getName().string(); }
+ // A unique id for this connection.
+ uint16_t id;
+
Status status;
sp<InputChannel> inputChannel;
@@ -91,29 +95,34 @@
// The sequence number of the current event being dispatched.
// This is used as part of the finished token as a way to determine whether the finished
// token is still valid before sending a finished signal back to the publisher.
- uint32_t messageSeqNum;
+ uint16_t messageSeqNum;
// True if a message has been received from the publisher but not yet finished.
bool messageInProgress;
};
Mutex mLock;
+ uint16_t mNextConnectionId;
KeyedVector<int32_t, sp<Connection> > mConnectionsByReceiveFd;
+ ssize_t getConnectionIndex(const sp<InputChannel>& inputChannel);
+
static void handleInputChannelDisposed(JNIEnv* env,
jobject inputChannelObj, const sp<InputChannel>& inputChannel, void* data);
static bool handleReceiveCallback(int receiveFd, int events, void* data);
- static jlong generateFinishedToken(int32_t receiveFd, int32_t messageSeqNum);
+ static jlong generateFinishedToken(int32_t receiveFd,
+ uint16_t connectionId, uint16_t messageSeqNum);
static void parseFinishedToken(jlong finishedToken,
- int32_t* outReceiveFd, uint32_t* outMessageIndex);
+ int32_t* outReceiveFd, uint16_t* outConnectionId, uint16_t* outMessageIndex);
};
// ----------------------------------------------------------------------------
-NativeInputQueue::NativeInputQueue() {
+NativeInputQueue::NativeInputQueue() :
+ mNextConnectionId(0) {
}
NativeInputQueue::~NativeInputQueue() {
@@ -134,18 +143,17 @@
sp<PollLoop> pollLoop = android_os_MessageQueue_getPollLoop(env, messageQueueObj);
- int receiveFd;
{ // acquire lock
AutoMutex _l(mLock);
- receiveFd = inputChannel->getReceivePipeFd();
- if (mConnectionsByReceiveFd.indexOfKey(receiveFd) >= 0) {
+ if (getConnectionIndex(inputChannel) >= 0) {
LOGW("Attempted to register already registered input channel '%s'",
inputChannel->getName().string());
return BAD_VALUE;
}
- sp<Connection> connection = new Connection(inputChannel, pollLoop);
+ uint16_t connectionId = mNextConnectionId++;
+ sp<Connection> connection = new Connection(connectionId, inputChannel, pollLoop);
status_t result = connection->inputConsumer.initialize();
if (result) {
LOGW("Failed to initialize input consumer for input channel '%s', status=%d",
@@ -155,13 +163,14 @@
connection->inputHandlerObjGlobal = env->NewGlobalRef(inputHandlerObj);
+ int32_t receiveFd = inputChannel->getReceivePipeFd();
mConnectionsByReceiveFd.add(receiveFd, connection);
+
+ pollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this);
} // release lock
android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
handleInputChannelDisposed, this);
-
- pollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this);
return OK;
}
@@ -177,38 +186,56 @@
LOGD("channel '%s' - Unregistered", inputChannel->getName().string());
#endif
- int32_t receiveFd;
- sp<Connection> connection;
{ // acquire lock
AutoMutex _l(mLock);
- receiveFd = inputChannel->getReceivePipeFd();
- ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(receiveFd);
+ ssize_t connectionIndex = getConnectionIndex(inputChannel);
if (connectionIndex < 0) {
LOGW("Attempted to unregister already unregistered input channel '%s'",
inputChannel->getName().string());
return BAD_VALUE;
}
- connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+ sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
mConnectionsByReceiveFd.removeItemsAt(connectionIndex);
connection->status = Connection::STATUS_ZOMBIE;
+ connection->pollLoop->removeCallback(inputChannel->getReceivePipeFd());
+
env->DeleteGlobalRef(connection->inputHandlerObjGlobal);
connection->inputHandlerObjGlobal = NULL;
+
+ if (connection->messageInProgress) {
+ LOGI("Sending finished signal for input channel '%s' since it is being unregistered "
+ "while an input message is still in progress.",
+ connection->getInputChannelName());
+ connection->messageInProgress = false;
+ connection->inputConsumer.sendFinishedSignal(); // ignoring result
+ }
} // release lock
android_view_InputChannel_setDisposeCallback(env, inputChannelObj, NULL, NULL);
-
- connection->pollLoop->removeCallback(receiveFd);
return OK;
}
+ssize_t NativeInputQueue::getConnectionIndex(const sp<InputChannel>& inputChannel) {
+ ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(inputChannel->getReceivePipeFd());
+ if (connectionIndex >= 0) {
+ sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+ if (connection->inputChannel.get() == inputChannel.get()) {
+ return connectionIndex;
+ }
+ }
+
+ return -1;
+}
+
status_t NativeInputQueue::finished(JNIEnv* env, jlong finishedToken, bool ignoreSpuriousFinish) {
int32_t receiveFd;
- uint32_t messageSeqNum;
- parseFinishedToken(finishedToken, &receiveFd, &messageSeqNum);
+ uint16_t connectionId;
+ uint16_t messageSeqNum;
+ parseFinishedToken(finishedToken, &receiveFd, &connectionId, &messageSeqNum);
{ // acquire lock
AutoMutex _l(mLock);
@@ -216,16 +243,25 @@
ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(receiveFd);
if (connectionIndex < 0) {
if (! ignoreSpuriousFinish) {
- LOGW("Attempted to finish input on channel that is no longer registered.");
+ LOGI("Ignoring finish signal on channel that is no longer registered.");
}
return DEAD_OBJECT;
}
sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+ if (connectionId != connection->id) {
+ if (! ignoreSpuriousFinish) {
+ LOGI("Ignoring finish signal on channel that is no longer registered.");
+ }
+ return DEAD_OBJECT;
+ }
+
if (messageSeqNum != connection->messageSeqNum || ! connection->messageInProgress) {
if (! ignoreSpuriousFinish) {
- LOGW("Attempted to finish input twice on channel '%s'.",
- connection->getInputChannelName());
+ LOGW("Attempted to finish input twice on channel '%s'. "
+ "finished messageSeqNum=%d, current messageSeqNum=%d, messageInProgress=%d",
+ connection->getInputChannelName(),
+ messageSeqNum, connection->messageSeqNum, connection->messageInProgress);
}
return INVALID_OPERATION;
}
@@ -312,7 +348,7 @@
connection->messageInProgress = true;
connection->messageSeqNum += 1;
- finishedToken = generateFinishedToken(receiveFd, connection->messageSeqNum);
+ finishedToken = generateFinishedToken(receiveFd, connection->id, connection->messageSeqNum);
inputHandlerObjLocal = env->NewLocalRef(connection->inputHandlerObjGlobal);
} // release lock
@@ -384,20 +420,23 @@
return true;
}
-jlong NativeInputQueue::generateFinishedToken(int32_t receiveFd, int32_t messageSeqNum) {
- return (jlong(receiveFd) << 32) | jlong(messageSeqNum);
+jlong NativeInputQueue::generateFinishedToken(int32_t receiveFd, uint16_t connectionId,
+ uint16_t messageSeqNum) {
+ return (jlong(receiveFd) << 32) | (jlong(connectionId) << 16) | jlong(messageSeqNum);
}
void NativeInputQueue::parseFinishedToken(jlong finishedToken,
- int32_t* outReceiveFd, uint32_t* outMessageIndex) {
+ int32_t* outReceiveFd, uint16_t* outConnectionId, uint16_t* outMessageIndex) {
*outReceiveFd = int32_t(finishedToken >> 32);
- *outMessageIndex = uint32_t(finishedToken & 0xffffffff);
+ *outConnectionId = uint16_t(finishedToken >> 16);
+ *outMessageIndex = uint16_t(finishedToken);
}
// ----------------------------------------------------------------------------
-NativeInputQueue::Connection::Connection(const sp<InputChannel>& inputChannel, const sp<PollLoop>& pollLoop) :
- status(STATUS_NORMAL), inputChannel(inputChannel), inputConsumer(inputChannel),
+NativeInputQueue::Connection::Connection(uint16_t id,
+ const sp<InputChannel>& inputChannel, const sp<PollLoop>& pollLoop) :
+ id(id), status(STATUS_NORMAL), inputChannel(inputChannel), inputConsumer(inputChannel),
pollLoop(pollLoop), inputHandlerObjGlobal(NULL),
messageSeqNum(0), messageInProgress(false) {
}
diff --git a/core/tests/coretests/res/raw/v30_multibyte_param.vcf b/core/tests/coretests/res/raw/v30_multibyte_param.vcf
new file mode 100644
index 0000000..cd200e5
--- /dev/null
+++ b/core/tests/coretests/res/raw/v30_multibyte_param.vcf
@@ -0,0 +1,5 @@
+BEGIN:VCARD
+VERSION:3.0
+N:F;G;M;;
+TEL;TYPE="费":1
+END:VCARD
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java b/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java
index 21f2254..e0e1f87 100644
--- a/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java
+++ b/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java
@@ -1008,4 +1008,26 @@
.put(Phone.TYPE, Phone.TYPE_PAGER)
.put(Phone.NUMBER, "6101231234@pagersample.com");
}
+
+ public void testMultiBytePropV30_Parse() {
+ mVerifier.initForImportTest(V30, R.raw.v30_multibyte_param);
+ mVerifier.addPropertyNodesVerifierElem()
+ .addExpectedNodeWithOrder("VERSION", "3.0")
+ .addExpectedNodeWithOrder("N", Arrays.asList("F", "G", "M", "", ""))
+ .addExpectedNodeWithOrder("TEL", "1", new TypeSet("\u8D39"));
+ }
+
+ public void testMultiBytePropV30() {
+ mVerifier.initForImportTest(V30, R.raw.v30_multibyte_param);
+ final ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
+ elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.FAMILY_NAME, "F")
+ .put(StructuredName.MIDDLE_NAME, "M")
+ .put(StructuredName.GIVEN_NAME, "G")
+ .put(StructuredName.DISPLAY_NAME, "G M F");
+ elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+ .put(Phone.LABEL, "\u8D39")
+ .put(Phone.NUMBER, "1");
+ }
}
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardUtilsTests.java b/core/tests/coretests/src/android/pim/vcard/VCardUtilsTests.java
index 59299f9..e805bee 100644
--- a/core/tests/coretests/src/android/pim/vcard/VCardUtilsTests.java
+++ b/core/tests/coretests/src/android/pim/vcard/VCardUtilsTests.java
@@ -82,4 +82,34 @@
assertFalse(VCardUtils.containsOnlyAlphaDigitHyphen(String.valueOf((char)i)));
}
}
+
+ public void testToStringAvailableAsV30ParamValue() {
+ // Smoke tests.
+ assertEquals("HOME", VCardUtils.toStringAvailableAsV30ParameValue("HOME"));
+ assertEquals("TEL", VCardUtils.toStringAvailableAsV30ParameValue("TEL"));
+ assertEquals("PAGER", VCardUtils.toStringAvailableAsV30ParameValue("PAGER"));
+
+ assertEquals("\"\"", VCardUtils.toStringAvailableAsV30ParameValue(""));
+
+ // non-Ascii must be allowed
+ assertEquals("\u4E8B\u52D9\u6240",
+ VCardUtils.toStringAvailableAsV30ParameValue("\u4E8B\u52D9\u6240"));
+ // Reported as bug report.
+ assertEquals("\u8D39", VCardUtils.toStringAvailableAsV30ParameValue("\u8D39"));
+ assertEquals("\"comma,separated\"",
+ VCardUtils.toStringAvailableAsV30ParameValue("comma,separated"));
+ assertEquals("\"colon:aware\"",
+ VCardUtils.toStringAvailableAsV30ParameValue("colon:aware"));
+ // CTL characters.
+ assertEquals("CTLExample",
+ VCardUtils.toStringAvailableAsV30ParameValue("CTL\u0001Example"));
+ // DQUOTE must be removed.
+ assertEquals("quoted",
+ VCardUtils.toStringAvailableAsV30ParameValue("\"quoted\""));
+ // DQUOTE must be removed basically, but we should detect a space, which
+ // require us to use DQUOTE again.
+ // Right-side has one more illegal dquote to test quote-handle code thoroughly.
+ assertEquals("\"Already quoted\"",
+ VCardUtils.toStringAvailableAsV30ParameValue("\"Already quoted\"\""));
+ }
}
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 2313f4c..8982388 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -27,6 +27,7 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.FileNotFoundException;
/**
* Creates Bitmap objects from various sources, including files, streams,
@@ -581,6 +582,139 @@
nativeSetDefaultConfig(config.nativeInt);
}
+ /**
+ * Create a LargeBitmap from the specified byte array.
+ * Currently only the Jpeg format is supported.
+ *
+ * @param data byte array of compressed image data.
+ * @param offset offset into data for where the decoder should begin
+ * parsing.
+ * @param length the number of bytes, beginning at offset, to parse
+ * @param isShareable If this is true, then the LargeBitmap may keep a
+ * shallow reference to the input. If this is false,
+ * then the LargeBitmap will explicitly make a copy of the
+ * input data, and keep that. Even if sharing is allowed,
+ * the implementation may still decide to make a deep
+ * copy of the input data. If an image is progressively encoded,
+ * allowing sharing may degrade the decoding speed.
+ * @return LargeBitmap, or null if the image data could not be decoded.
+ * @throws IOException if the image format is not supported or can not be decoded.
+ * @hide
+ */
+ public static LargeBitmap createLargeBitmap(byte[] data,
+ int offset, int length, boolean isShareable) throws IOException {
+ if ((offset | length) < 0 || data.length < offset + length) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ return nativeCreateLargeBitmap(data, offset, length, isShareable);
+ }
+
+ /**
+ * Create a LargeBitmap from the file descriptor.
+ * The position within the descriptor will not be changed when
+ * this returns, so the descriptor can be used again as is.
+ * Currently only the Jpeg format is supported.
+ *
+ * @param fd The file descriptor containing the data to decode
+ * @param isShareable If this is true, then the LargeBitmap may keep a
+ * shallow reference to the input. If this is false,
+ * then the LargeBitmap will explicitly make a copy of the
+ * input data, and keep that. Even if sharing is allowed,
+ * the implementation may still decide to make a deep
+ * copy of the input data. If an image is progressively encoded,
+ * allowing sharing may degrade the decoding speed.
+ * @return LargeBitmap, or null if the image data could not be decoded.
+ * @throws IOException if the image format is not supported or can not be decoded.
+ * @hide
+ */
+ public static LargeBitmap createLargeBitmap(
+ FileDescriptor fd, boolean isShareable) throws IOException {
+ if (MemoryFile.isMemoryFile(fd)) {
+ int mappedlength = MemoryFile.getSize(fd);
+ MemoryFile file = new MemoryFile(fd, mappedlength, "r");
+ InputStream is = file.getInputStream();
+ return createLargeBitmap(is, isShareable);
+ }
+ return nativeCreateLargeBitmap(fd, isShareable);
+ }
+
+ /**
+ * Create a LargeBitmap from an input stream.
+ * The stream's position will be where ever it was after the encoded data
+ * was read.
+ * Currently only the Jpeg format is supported.
+ *
+ * @param is The input stream that holds the raw data to be decoded into a
+ * LargeBitmap.
+ * @param isShareable If this is true, then the LargeBitmap may keep a
+ * shallow reference to the input. If this is false,
+ * then the LargeBitmap will explicitly make a copy of the
+ * input data, and keep that. Even if sharing is allowed,
+ * the implementation may still decide to make a deep
+ * copy of the input data. If an image is progressively encoded,
+ * allowing sharing may degrade the decoding speed.
+ * @return LargeBitmap, or null if the image data could not be decoded.
+ * @throws IOException if the image format is not supported or can not be decoded.
+ * @hide
+ */
+ public static LargeBitmap createLargeBitmap(InputStream is,
+ boolean isShareable) throws IOException {
+ // we need mark/reset to work properly in JNI
+
+ if (!is.markSupported()) {
+ is = new BufferedInputStream(is, 16 * 1024);
+ }
+
+ if (is instanceof AssetManager.AssetInputStream) {
+ return nativeCreateLargeBitmap(
+ ((AssetManager.AssetInputStream) is).getAssetInt(),
+ isShareable);
+ } else {
+ // pass some temp storage down to the native code. 1024 is made up,
+ // but should be large enough to avoid too many small calls back
+ // into is.read(...).
+ byte [] tempStorage = null;
+ tempStorage = new byte[16 * 1024];
+ return nativeCreateLargeBitmap(is, tempStorage, isShareable);
+ }
+ }
+
+ /**
+ * Create a LargeBitmap from a file path.
+ * Currently only the Jpeg format is supported.
+ *
+ * @param pathName complete path name for the file to be decoded.
+ * @param isShareable If this is true, then the LargeBitmap may keep a
+ * shallow reference to the input. If this is false,
+ * then the LargeBitmap will explicitly make a copy of the
+ * input data, and keep that. Even if sharing is allowed,
+ * the implementation may still decide to make a deep
+ * copy of the input data. If an image is progressively encoded,
+ * allowing sharing may degrade the decoding speed.
+ * @return LargeBitmap, or null if the image data could not be decoded.
+ * @throws IOException if the image format is not supported or can not be decoded.
+ * @hide
+ */
+ public static LargeBitmap createLargeBitmap(String pathName,
+ boolean isShareable) throws FileNotFoundException, IOException {
+ LargeBitmap bm = null;
+ InputStream stream = null;
+
+ try {
+ stream = new FileInputStream(pathName);
+ bm = createLargeBitmap(stream, isShareable);
+ } finally {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ // do nothing here
+ }
+ }
+ }
+ return bm;
+ }
+
private static native void nativeSetDefaultConfig(int nativeConfig);
private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
Rect padding, Options opts);
@@ -590,5 +724,14 @@
private static native Bitmap nativeDecodeByteArray(byte[] data, int offset,
int length, Options opts);
private static native byte[] nativeScaleNinePatch(byte[] chunk, float scale, Rect pad);
+
+ private static native LargeBitmap nativeCreateLargeBitmap(
+ byte[] data, int offset, int length, boolean isShareable);
+ private static native LargeBitmap nativeCreateLargeBitmap(
+ FileDescriptor fd, boolean isShareable);
+ private static native LargeBitmap nativeCreateLargeBitmap(
+ InputStream is, byte[] storage, boolean isShareable);
+ private static native LargeBitmap nativeCreateLargeBitmap(
+ int asset, boolean isShareable);
}
diff --git a/graphics/java/android/graphics/LargeBitmap.java b/graphics/java/android/graphics/LargeBitmap.java
new file mode 100644
index 0000000..6656b17
--- /dev/null
+++ b/graphics/java/android/graphics/LargeBitmap.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.DisplayMetrics;
+
+import java.io.OutputStream;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
+
+/**
+ * LargeBitmap can be used to decode a rectangle region from an image.
+ * LargeBimap is particularly useful when an original image is large and
+ * you only need parts of the image.
+ *
+ * To create a LargeBitmap, call BitmapFactory.createLargeBitmap().
+ * Given a LargeBitmap, users can call decodeRegion() repeatedly
+ * to get a decoded Bitmap of the specified region.
+ * @hide
+ */
+public final class LargeBitmap {
+ private int mNativeLargeBitmap;
+ private boolean mRecycled;
+
+ /* Private constructor that must received an already allocated native
+ large bitmap int (pointer).
+
+ This can be called from JNI code.
+ */
+ private LargeBitmap(int lbm) {
+ mNativeLargeBitmap = lbm;
+ mRecycled = false;
+ }
+
+ /**
+ * Decodes a rectangle region in the image specified by rect.
+ *
+ * @param rect The rectangle that specified the region to be decode.
+ * @param opts null-ok; Options that control downsampling.
+ * inPurgeable is not supported.
+ * @return The decoded bitmap, or null if the image data could not be
+ * decoded.
+ */
+ public Bitmap decodeRegion(Rect rect, BitmapFactory.Options options) {
+ checkRecycled("decodeRegion called on recycled large bitmap");
+ if (rect.left < 0 || rect.top < 0 || rect.right > getWidth() || rect.bottom > getHeight())
+ throw new IllegalArgumentException("rectangle is not inside the image");
+ return nativeDecodeRegion(mNativeLargeBitmap, rect.left, rect.top,
+ rect.right - rect.left, rect.bottom - rect.top, options);
+ }
+
+ /** Returns the original image's width */
+ public int getWidth() {
+ checkRecycled("getWidth called on recycled large bitmap");
+ return nativeGetWidth(mNativeLargeBitmap);
+ }
+
+ /** Returns the original image's height */
+ public int getHeight() {
+ checkRecycled("getHeight called on recycled large bitmap");
+ return nativeGetHeight(mNativeLargeBitmap);
+ }
+
+ /**
+ * Frees up the memory associated with this large bitmap, and mark the
+ * large bitmap as "dead", meaning it will throw an exception if decodeRegion(),
+ * getWidth() or getHeight() is called.
+ * This operation cannot be reversed, so it should only be called if you are
+ * sure there are no further uses for the large bitmap. This is an advanced call,
+ * and normally need not be called, since the normal GC process will free up this
+ * memory when there are no more references to this bitmap.
+ */
+ public void recycle() {
+ if (!mRecycled) {
+ nativeClean(mNativeLargeBitmap);
+ mRecycled = true;
+ }
+ }
+
+ /**
+ * Returns true if this large bitmap has been recycled.
+ * If so, then it is an error to try use its method.
+ *
+ * @return true if the large bitmap has been recycled
+ */
+ public final boolean isRecycled() {
+ return mRecycled;
+ }
+
+ /**
+ * Called by methods that want to throw an exception if the bitmap
+ * has already been recycled.
+ */
+ private void checkRecycled(String errorMessage) {
+ if (mRecycled) {
+ throw new IllegalStateException(errorMessage);
+ }
+ }
+
+ protected void finalize() {
+ recycle();
+ }
+
+ private static native Bitmap nativeDecodeRegion(int lbm,
+ int start_x, int start_y, int width, int height,
+ BitmapFactory.Options options);
+ private static native int nativeGetWidth(int lbm);
+ private static native int nativeGetHeight(int lbm);
+ private static native void nativeClean(int lbm);
+}
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index 6a5d254..99a3115 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -337,11 +337,14 @@
static const char PIXEL_FORMAT_JPEG[];
// Values for focus mode settings.
- // Auto-focus mode.
+ // Auto-focus mode. Applications should call
+ // CameraHardwareInterface.autoFocus to start the focus in this mode.
static const char FOCUS_MODE_AUTO[];
// Focus is set at infinity. Applications should not call
// CameraHardwareInterface.autoFocus in this mode.
static const char FOCUS_MODE_INFINITY[];
+ // Macro (close-up) focus mode. Applications should call
+ // CameraHardwareInterface.autoFocus to start the focus in this mode.
static const char FOCUS_MODE_MACRO[];
// Focus is fixed. The camera is always in this mode if the focus is not
// adjustable. If the camera has auto-focus, this mode can fix the
@@ -355,7 +358,8 @@
// Continuous auto focus mode. The camera continuously tries to focus. This
// is ideal for shooting video or shooting photo of moving object. Auto
// focus starts when the parameter is set. Applications should not call
- // CameraHardwareInterface.autoFocus in this mode.
+ // CameraHardwareInterface.autoFocus in this mode. To stop continuous
+ // focus, applications should change the focus mode to other modes.
static const char FOCUS_MODE_CONTINUOUS[];
// The camera determines the exposure by giving more weight to the
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index 2e1e8d8..be96935 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -128,6 +128,12 @@
// Write the first chunk from the given ChunkInfo.
void writeFirstChunk(ChunkInfo* info);
+ // Adjust other track media clock (presumably wall clock)
+ // based on audio track media clock with the drift time.
+ int64_t mDriftTimeUs;
+ void addDriftTimeUs(int64_t driftTimeUs);
+ int64_t getDriftTimeUs();
+
void lock();
void unlock();
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index ab1fa4f..43354c2 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -86,10 +86,10 @@
// Track authoring progress status
// kKeyTrackTimeStatus is used to track progress in elapsed time
- // kKeyTrackFrameStatus is used to track progress in authored frames
- kKeyTrackFrameStatus = 'tkfm', // int32_t
kKeyTrackTimeStatus = 'tktm', // int64_t
+ kKeyNotRealTime = 'ntrt', // bool (int32_t)
+
};
enum {
diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h
index dab35b3..3d42856 100644
--- a/include/ui/EventHub.h
+++ b/include/ui/EventHub.h
@@ -224,6 +224,7 @@
uint8_t* keyBitmask;
KeyLayoutMap* layoutMap;
String8 keylayoutFilename;
+ int fd;
device_t* next;
device_t(int32_t _id, const char* _path, const char* name);
@@ -266,6 +267,12 @@
#ifdef EV_SW
int32_t mSwitches[SW_MAX + 1];
#endif
+
+ static const int INPUT_BUFFER_SIZE = 64;
+ struct input_event mInputBufferData[INPUT_BUFFER_SIZE];
+ int32_t mInputBufferIndex;
+ int32_t mInputBufferCount;
+ int32_t mInputDeviceIndex;
};
}; // namespace android
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index d3495fe..2505cb0 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -554,6 +554,8 @@
// All registered connections mapped by receive pipe file descriptor.
KeyedVector<int, sp<Connection> > mConnectionsByReceiveFd;
+ ssize_t getConnectionIndex(const sp<InputChannel>& inputChannel);
+
// Active connections are connections that have a non-empty outbound queue.
// We don't use a ref-counted pointer here because we explicitly abort connections
// during unregistration which causes the connection's outbound queue to be cleared
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index 71c6c51..56d2765 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -480,10 +480,6 @@
inline void clear() {
fields = 0;
}
-
- inline bool isDirty() {
- return fields != 0;
- }
} mAccumulator;
float mXScale;
@@ -702,7 +698,7 @@
} historyData[AVERAGING_HISTORY_SIZE];
} mAveragingTouchFilter;
- struct JumpTouchFilterState {
+ struct JumpyTouchFilterState {
uint32_t jumpyPointsDropped;
} mJumpyTouchFilter;
@@ -765,10 +761,6 @@
inline void clear() {
fields = 0;
}
-
- inline bool isDirty() {
- return fields != 0;
- }
} mAccumulator;
bool mDown;
@@ -804,7 +796,8 @@
FIELD_ABS_MT_WIDTH_MAJOR = 16,
FIELD_ABS_MT_WIDTH_MINOR = 32,
FIELD_ABS_MT_ORIENTATION = 64,
- FIELD_ABS_MT_TRACKING_ID = 128
+ FIELD_ABS_MT_TRACKING_ID = 128,
+ FIELD_ABS_MT_PRESSURE = 256,
};
uint32_t pointerCount;
@@ -819,6 +812,7 @@
int32_t absMTWidthMinor;
int32_t absMTOrientation;
int32_t absMTTrackingId;
+ int32_t absMTPressure;
inline void clear() {
fields = 0;
@@ -829,10 +823,6 @@
pointerCount = 0;
pointers[0].clear();
}
-
- inline bool isDirty() {
- return pointerCount != 0;
- }
} mAccumulator;
void initialize();
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index 124f7b3..891661d 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -60,7 +60,6 @@
#define ID_MASK 0x0000ffff
#define SEQ_MASK 0x7fff0000
#define SEQ_SHIFT 16
-#define id_to_index(id) ((id&ID_MASK)+1)
#ifndef ABS_MT_TOUCH_MAJOR
#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */
@@ -87,7 +86,7 @@
EventHub::device_t::device_t(int32_t _id, const char* _path, const char* name)
: id(_id), path(_path), name(name), classes(0)
- , keyBitmask(NULL), layoutMap(new KeyLayoutMap()), next(NULL) {
+ , keyBitmask(NULL), layoutMap(new KeyLayoutMap()), fd(-1), next(NULL) {
}
EventHub::device_t::~device_t() {
@@ -100,6 +99,7 @@
, mDevicesById(0), mNumDevicesById(0)
, mOpeningDevices(0), mClosingDevices(0)
, mDevices(0), mFDs(0), mFDCount(0), mOpened(false)
+ , mInputBufferIndex(0), mInputBufferCount(0), mInputDeviceIndex(0)
{
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
#ifdef EV_SW
@@ -151,9 +151,9 @@
struct input_absinfo info;
- if(ioctl(mFDs[id_to_index(device->id)].fd, EVIOCGABS(axis), &info)) {
+ if(ioctl(device->fd, EVIOCGABS(axis), &info)) {
LOGW("Error reading absolute controller %d for device %s fd %d\n",
- axis, device->name.string(), mFDs[id_to_index(device->id)].fd);
+ axis, device->name.string(), device->fd);
return -errno;
}
@@ -182,7 +182,7 @@
int32_t EventHub::getScanCodeStateLocked(device_t* device, int32_t scanCode) const {
uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
memset(key_bitmask, 0, sizeof(key_bitmask));
- if (ioctl(mFDs[id_to_index(device->id)].fd,
+ if (ioctl(device->fd,
EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
return test_bit(scanCode, key_bitmask) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
}
@@ -205,8 +205,7 @@
uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
memset(key_bitmask, 0, sizeof(key_bitmask));
- if (ioctl(mFDs[id_to_index(device->id)].fd,
- EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
+ if (ioctl(device->fd, EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
#if 0
for (size_t i=0; i<=KEY_MAX; i++) {
LOGI("(Scan code %d: down=%d)", i, test_bit(i, key_bitmask));
@@ -242,7 +241,7 @@
int32_t EventHub::getSwitchStateLocked(device_t* device, int32_t sw) const {
uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)];
memset(sw_bitmask, 0, sizeof(sw_bitmask));
- if (ioctl(mFDs[id_to_index(device->id)].fd,
+ if (ioctl(device->fd,
EVIOCGSW(sizeof(sw_bitmask)), sw_bitmask) >= 0) {
return test_bit(sw, sw_bitmask) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
}
@@ -343,13 +342,6 @@
outEvent->value = 0;
outEvent->when = 0;
- status_t err;
-
- int i;
- int res;
- int pollres;
- struct input_event iev;
-
// Note that we only allow one caller to getEvent(), so don't need
// to do locking here... only when adding/removing devices.
@@ -358,9 +350,8 @@
mOpened = true;
}
- while(1) {
-
- // First, report any devices that had last been added/removed.
+ for (;;) {
+ // Report any devices that had last been added/removed.
if (mClosingDevices != NULL) {
device_t* device = mClosingDevices;
LOGV("Reporting device closed: id=0x%x, name=%s\n",
@@ -390,77 +381,96 @@
return true;
}
- release_wake_lock(WAKE_LOCK_ID);
+ // Grab the next input event.
+ for (;;) {
+ // Consume buffered input events, if any.
+ if (mInputBufferIndex < mInputBufferCount) {
+ const struct input_event& iev = mInputBufferData[mInputBufferIndex++];
+ const device_t* device = mDevices[mInputDeviceIndex];
- pollres = poll(mFDs, mFDCount, -1);
-
- acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
-
- if (pollres <= 0) {
- if (errno != EINTR) {
- LOGW("select failed (errno=%d)\n", errno);
- usleep(100000);
- }
- continue;
- }
-
- //printf("poll %d, returned %d\n", mFDCount, pollres);
-
- // mFDs[0] is used for inotify, so process regular events starting at mFDs[1]
- for(i = 1; i < mFDCount; i++) {
- if(mFDs[i].revents) {
- LOGV("revents for %d = 0x%08x", i, mFDs[i].revents);
- if(mFDs[i].revents & POLLIN) {
- res = read(mFDs[i].fd, &iev, sizeof(iev));
- if (res == sizeof(iev)) {
- device_t* device = mDevices[i];
- LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d",
- device->path.string(),
- (int) iev.time.tv_sec, (int) iev.time.tv_usec,
- iev.type, iev.code, iev.value);
- if (device->id == mFirstKeyboardId) {
- outEvent->deviceId = 0;
- } else {
- outEvent->deviceId = device->id;
- }
- outEvent->type = iev.type;
- outEvent->scanCode = iev.code;
- if (iev.type == EV_KEY) {
- err = device->layoutMap->map(iev.code,
- & outEvent->keyCode, & outEvent->flags);
- LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",
- iev.code, outEvent->keyCode, outEvent->flags, err);
- if (err != 0) {
- outEvent->keyCode = AKEYCODE_UNKNOWN;
- outEvent->flags = 0;
- }
- } else {
- outEvent->keyCode = iev.code;
- }
- outEvent->value = iev.value;
-
- // Use an event timestamp in the same timebase as
- // java.lang.System.nanoTime() and android.os.SystemClock.uptimeMillis()
- // as expected by the rest of the system.
- outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
- return true;
- } else {
- if (res<0) {
- LOGW("could not get event (errno=%d)", errno);
- } else {
- LOGE("could not get event (wrong size: %d)", res);
- }
- continue;
+ LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d", device->path.string(),
+ (int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value);
+ if (device->id == mFirstKeyboardId) {
+ outEvent->deviceId = 0;
+ } else {
+ outEvent->deviceId = device->id;
+ }
+ outEvent->type = iev.type;
+ outEvent->scanCode = iev.code;
+ if (iev.type == EV_KEY) {
+ status_t err = device->layoutMap->map(iev.code,
+ & outEvent->keyCode, & outEvent->flags);
+ LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",
+ iev.code, outEvent->keyCode, outEvent->flags, err);
+ if (err != 0) {
+ outEvent->keyCode = AKEYCODE_UNKNOWN;
+ outEvent->flags = 0;
}
+ } else {
+ outEvent->keyCode = iev.code;
+ }
+ outEvent->value = iev.value;
+
+ // Use an event timestamp in the same timebase as
+ // java.lang.System.nanoTime() and android.os.SystemClock.uptimeMillis()
+ // as expected by the rest of the system.
+ outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
+ return true;
+ }
+
+ // Finish reading all events from devices identified in previous poll().
+ // This code assumes that mInputDeviceIndex is initially 0 and that the
+ // revents member of pollfd is initialized to 0 when the device is first added.
+ // Since mFDs[0] is used for inotify, we process regular events starting at index 1.
+ mInputDeviceIndex += 1;
+ if (mInputDeviceIndex >= mFDCount) {
+ mInputDeviceIndex = 0;
+ break;
+ }
+
+ const struct pollfd &pfd = mFDs[mInputDeviceIndex];
+ if (pfd.revents & POLLIN) {
+ int32_t readSize = read(pfd.fd, mInputBufferData,
+ sizeof(struct input_event) * INPUT_BUFFER_SIZE);
+ if (readSize < 0) {
+ if (errno != EAGAIN && errno != EINTR) {
+ LOGW("could not get event (errno=%d)", errno);
+ }
+ } else if ((readSize % sizeof(struct input_event)) != 0) {
+ LOGE("could not get event (wrong size: %d)", readSize);
+ } else {
+ mInputBufferCount = readSize / sizeof(struct input_event);
+ mInputBufferIndex = 0;
}
}
}
-
+
// read_notify() will modify mFDs and mFDCount, so this must be done after
// processing all other events.
if(mFDs[0].revents & POLLIN) {
read_notify(mFDs[0].fd);
}
+
+ // Poll for events. Mind the wake lock dance!
+ // We hold a wake lock at all times except during poll(). This works due to some
+ // subtle choreography. When a device driver has pending (unread) events, it acquires
+ // a kernel wake lock. However, once the last pending event has been read, the device
+ // driver will release the kernel wake lock. To prevent the system from going to sleep
+ // when this happens, the EventHub holds onto its own user wake lock while the client
+ // is processing events. Thus the system can only sleep if there are no events
+ // pending or currently being processed.
+ release_wake_lock(WAKE_LOCK_ID);
+
+ int pollResult = poll(mFDs, mFDCount, -1);
+
+ acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
+
+ if (pollResult <= 0) {
+ if (errno != EINTR) {
+ LOGW("select failed (errno=%d)\n", errno);
+ usleep(100000);
+ }
+ }
}
}
@@ -478,6 +488,7 @@
mFDs = (pollfd *)calloc(1, sizeof(mFDs[0]));
mDevices = (device_t **)calloc(1, sizeof(mDevices[0]));
mFDs[0].events = POLLIN;
+ mFDs[0].revents = 0;
mDevices[0] = NULL;
#ifdef HAVE_INOTIFY
mFDs[0].fd = inotify_init();
@@ -570,7 +581,6 @@
if (strcmp(name, test) == 0) {
LOGI("ignoring event id %s driver %s\n", deviceName, test);
close(fd);
- fd = -1;
return -1;
}
}
@@ -584,6 +594,12 @@
idstr[0] = '\0';
}
+ if (fcntl(fd, F_SETFL, O_NONBLOCK)) {
+ LOGE("Error %d making device file descriptor non-blocking.", errno);
+ close(fd);
+ return -1;
+ }
+
int devid = 0;
while (devid < mNumDevicesById) {
if (mDevicesById[devid].device == NULL) {
@@ -638,8 +654,10 @@
return -1;
}
+ device->fd = fd;
mFDs[mFDCount].fd = fd;
mFDs[mFDCount].events = POLLIN;
+ mFDs[mFDCount].revents = 0;
// Figure out the kinds of events the device reports.
@@ -794,6 +812,14 @@
device->id, name, propName, keylayoutFilename);
}
+ // If the device isn't recognized as something we handle, don't monitor it.
+ if (device->classes == 0) {
+ LOGV("Dropping device %s %p, id = %d\n", deviceName, device, devid);
+ close(fd);
+ delete device;
+ return -1;
+ }
+
LOGI("New device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n",
deviceName, name, device->id, mNumDevicesById, mFDCount, fd, device->classes);
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index b53f140..13030b5 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -433,8 +433,7 @@
for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {
const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i);
- ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(
- inputTarget.inputChannel->getReceivePipeFd());
+ ssize_t connectionIndex = getConnectionIndex(inputTarget.inputChannel);
if (connectionIndex >= 0) {
sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,
@@ -1367,12 +1366,10 @@
LOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().string());
#endif
- int receiveFd;
{ // acquire lock
AutoMutex _l(mLock);
- receiveFd = inputChannel->getReceivePipeFd();
- if (mConnectionsByReceiveFd.indexOfKey(receiveFd) >= 0) {
+ if (getConnectionIndex(inputChannel) >= 0) {
LOGW("Attempted to register already registered input channel '%s'",
inputChannel->getName().string());
return BAD_VALUE;
@@ -1386,12 +1383,13 @@
return status;
}
+ int32_t receiveFd = inputChannel->getReceivePipeFd();
mConnectionsByReceiveFd.add(receiveFd, connection);
+ mPollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this);
+
runCommandsLockedInterruptible();
} // release lock
-
- mPollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this);
return OK;
}
@@ -1400,12 +1398,10 @@
LOGD("channel '%s' ~ unregisterInputChannel", inputChannel->getName().string());
#endif
- int32_t receiveFd;
{ // acquire lock
AutoMutex _l(mLock);
- receiveFd = inputChannel->getReceivePipeFd();
- ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(receiveFd);
+ ssize_t connectionIndex = getConnectionIndex(inputChannel);
if (connectionIndex < 0) {
LOGW("Attempted to unregister already unregistered input channel '%s'",
inputChannel->getName().string());
@@ -1417,20 +1413,32 @@
connection->status = Connection::STATUS_ZOMBIE;
+ mPollLoop->removeCallback(inputChannel->getReceivePipeFd());
+
nsecs_t currentTime = now();
abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
runCommandsLockedInterruptible();
} // release lock
- mPollLoop->removeCallback(receiveFd);
-
// Wake the poll loop because removing the connection may have changed the current
// synchronization state.
mPollLoop->wake();
return OK;
}
+ssize_t InputDispatcher::getConnectionIndex(const sp<InputChannel>& inputChannel) {
+ ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(inputChannel->getReceivePipeFd());
+ if (connectionIndex >= 0) {
+ sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+ if (connection->inputChannel.get() == inputChannel.get()) {
+ return connectionIndex;
+ }
+ }
+
+ return -1;
+}
+
void InputDispatcher::activateConnectionLocked(Connection* connection) {
for (size_t i = 0; i < mActiveConnections.size(); i++) {
if (mActiveConnections.itemAt(i) == connection) {
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 5f5a4ac..6f042ec 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -945,7 +945,6 @@
mAccumulator.fields = Accumulator::FIELD_BTN_MOUSE;
mAccumulator.btnMouse = false;
sync(when);
- mAccumulator.clear();
}
InputMapper::reset();
@@ -958,9 +957,9 @@
case BTN_MOUSE:
mAccumulator.fields |= Accumulator::FIELD_BTN_MOUSE;
mAccumulator.btnMouse = rawEvent->value != 0;
-
+ // Sync now since BTN_MOUSE is not necessarily followed by SYN_REPORT and
+ // we need to ensure that we report the up/down promptly.
sync(rawEvent->when);
- mAccumulator.clear();
break;
}
break;
@@ -981,10 +980,7 @@
case EV_SYN:
switch (rawEvent->scanCode) {
case SYN_REPORT:
- if (mAccumulator.isDirty()) {
- sync(rawEvent->when);
- mAccumulator.clear();
- }
+ sync(rawEvent->when);
break;
}
break;
@@ -992,13 +988,17 @@
}
void TrackballInputMapper::sync(nsecs_t when) {
+ uint32_t fields = mAccumulator.fields;
+ if (fields == 0) {
+ return; // no new state changes, so nothing to do
+ }
+
int motionEventAction;
PointerCoords pointerCoords;
nsecs_t downTime;
{ // acquire lock
AutoMutex _l(mLock);
- uint32_t fields = mAccumulator.fields;
bool downChanged = fields & Accumulator::FIELD_BTN_MOUSE;
if (downChanged) {
@@ -1061,6 +1061,8 @@
} // release lock
applyPolicyAndDispatch(when, motionEventAction, & pointerCoords, downTime);
+
+ mAccumulator.clear();
}
void TrackballInputMapper::applyPolicyAndDispatch(nsecs_t when, int32_t motionEventAction,
@@ -2380,8 +2382,8 @@
mDown = false;
mX = 0;
mY = 0;
- mPressure = 0;
- mSize = 0;
+ mPressure = 1; // default to 1 for devices that don't report pressure
+ mSize = 0; // default to 0 for devices that don't report size
}
void SingleTouchInputMapper::reset() {
@@ -2397,9 +2399,9 @@
case BTN_TOUCH:
mAccumulator.fields |= Accumulator::FIELD_BTN_TOUCH;
mAccumulator.btnTouch = rawEvent->value != 0;
-
- sync(rawEvent->when);
- mAccumulator.clear();
+ // Don't sync immediately. Wait until the next SYN_REPORT since we might
+ // not have received valid position information yet. This logic assumes that
+ // BTN_TOUCH is always followed by SYN_REPORT as part of a complete packet.
break;
}
break;
@@ -2428,10 +2430,7 @@
case EV_SYN:
switch (rawEvent->scanCode) {
case SYN_REPORT:
- if (mAccumulator.isDirty()) {
- sync(rawEvent->when);
- mAccumulator.clear();
- }
+ sync(rawEvent->when);
break;
}
break;
@@ -2439,9 +2438,10 @@
}
void SingleTouchInputMapper::sync(nsecs_t when) {
- /* Update device state */
-
uint32_t fields = mAccumulator.fields;
+ if (fields == 0) {
+ return; // no new state changes, so nothing to do
+ }
if (fields & Accumulator::FIELD_BTN_TOUCH) {
mDown = mAccumulator.btnTouch;
@@ -2472,8 +2472,8 @@
mCurrentTouch.pointers[0].y = mY;
mCurrentTouch.pointers[0].pressure = mPressure;
mCurrentTouch.pointers[0].size = mSize;
- mCurrentTouch.pointers[0].touchMajor = mPressure;
- mCurrentTouch.pointers[0].touchMinor = mPressure;
+ mCurrentTouch.pointers[0].touchMajor = mSize;
+ mCurrentTouch.pointers[0].touchMinor = mSize;
mCurrentTouch.pointers[0].toolMajor = mSize;
mCurrentTouch.pointers[0].toolMinor = mSize;
mCurrentTouch.pointers[0].orientation = 0;
@@ -2482,6 +2482,8 @@
}
syncTouch(when, true);
+
+ mAccumulator.clear();
}
void SingleTouchInputMapper::configureAxes() {
@@ -2494,8 +2496,8 @@
getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_PRESSURE, & mAxes.pressure);
getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_TOOL_WIDTH, & mAxes.size);
- mAxes.touchMajor = mAxes.pressure;
- mAxes.touchMinor = mAxes.pressure;
+ mAxes.touchMajor = mAxes.size;
+ mAxes.touchMinor = mAxes.size;
mAxes.toolMajor = mAxes.size;
mAxes.toolMinor = mAxes.size;
}
@@ -2585,10 +2587,7 @@
}
case SYN_REPORT:
- if (mAccumulator.isDirty()) {
- sync(rawEvent->when);
- mAccumulator.clear();
- }
+ sync(rawEvent->when);
break;
}
break;
@@ -2598,11 +2597,7 @@
void MultiTouchInputMapper::sync(nsecs_t when) {
static const uint32_t REQUIRED_FIELDS =
Accumulator::FIELD_ABS_MT_POSITION_X
- | Accumulator::FIELD_ABS_MT_POSITION_Y
- | Accumulator::FIELD_ABS_MT_TOUCH_MAJOR
- | Accumulator::FIELD_ABS_MT_WIDTH_MAJOR;
-
- /* Update device state */
+ | Accumulator::FIELD_ABS_MT_POSITION_Y;
uint32_t inCount = mAccumulator.pointerCount;
uint32_t outCount = 0;
@@ -2611,53 +2606,76 @@
mCurrentTouch.clear();
for (uint32_t inIndex = 0; inIndex < inCount; inIndex++) {
- uint32_t fields = mAccumulator.pointers[inIndex].fields;
+ const Accumulator::Pointer& inPointer = mAccumulator.pointers[inIndex];
+ uint32_t fields = inPointer.fields;
if ((fields & REQUIRED_FIELDS) != REQUIRED_FIELDS) {
-#if DEBUG_POINTERS
- LOGD("Pointers: Missing required multitouch pointer fields: index=%d, fields=%d",
- inIndex, fields);
- continue;
-#endif
- }
-
- if (mAccumulator.pointers[inIndex].absMTTouchMajor <= 0) {
- // Pointer is not down. Drop it.
+ // Some drivers send empty MT sync packets without X / Y to indicate a pointer up.
+ // Drop this finger.
continue;
}
- mCurrentTouch.pointers[outCount].x = mAccumulator.pointers[inIndex].absMTPositionX;
- mCurrentTouch.pointers[outCount].y = mAccumulator.pointers[inIndex].absMTPositionY;
+ PointerData& outPointer = mCurrentTouch.pointers[outCount];
+ outPointer.x = inPointer.absMTPositionX;
+ outPointer.y = inPointer.absMTPositionY;
- mCurrentTouch.pointers[outCount].touchMajor =
- mAccumulator.pointers[inIndex].absMTTouchMajor;
- mCurrentTouch.pointers[outCount].touchMinor =
- (fields & Accumulator::FIELD_ABS_MT_TOUCH_MINOR) != 0
- ? mAccumulator.pointers[inIndex].absMTTouchMinor
- : mAccumulator.pointers[inIndex].absMTTouchMajor;
+ if (fields & Accumulator::FIELD_ABS_MT_TOUCH_MAJOR) {
+ int32_t value = inPointer.absMTTouchMajor;
+ if (value <= 0) {
+ // Some devices send sync packets with X / Y but with a 0 touch major to indicate
+ // a pointer up. Drop this finger.
+ continue;
+ }
+ outPointer.touchMajor = inPointer.absMTTouchMajor;
+ } else {
+ outPointer.touchMajor = 0;
+ }
- mCurrentTouch.pointers[outCount].toolMajor =
- mAccumulator.pointers[inIndex].absMTWidthMajor;
- mCurrentTouch.pointers[outCount].toolMinor =
- (fields & Accumulator::FIELD_ABS_MT_WIDTH_MINOR) != 0
- ? mAccumulator.pointers[inIndex].absMTWidthMinor
- : mAccumulator.pointers[inIndex].absMTWidthMajor;
+ if (fields & Accumulator::FIELD_ABS_MT_TOUCH_MINOR) {
+ outPointer.touchMinor = inPointer.absMTTouchMinor;
+ } else {
+ outPointer.touchMinor = outPointer.touchMajor;
+ }
- mCurrentTouch.pointers[outCount].orientation =
- (fields & Accumulator::FIELD_ABS_MT_ORIENTATION) != 0
- ? mAccumulator.pointers[inIndex].absMTOrientation : 0;
+ if (fields & Accumulator::FIELD_ABS_MT_WIDTH_MAJOR) {
+ outPointer.toolMajor = inPointer.absMTWidthMajor;
+ } else {
+ outPointer.toolMajor = outPointer.touchMajor;
+ }
- // Derive an approximation of pressure and size.
- // FIXME assignment of pressure may be incorrect, probably better to let
- // pressure = touch / width. Later on we pass width to MotionEvent as a size, which
- // isn't quite right either. Should be using touch for that.
- mCurrentTouch.pointers[outCount].pressure = mAccumulator.pointers[inIndex].absMTTouchMajor;
- mCurrentTouch.pointers[outCount].size = mAccumulator.pointers[inIndex].absMTWidthMajor;
+ if (fields & Accumulator::FIELD_ABS_MT_WIDTH_MINOR) {
+ outPointer.toolMinor = inPointer.absMTWidthMinor;
+ } else {
+ outPointer.toolMinor = outPointer.toolMajor;
+ }
+
+ if (fields & Accumulator::FIELD_ABS_MT_ORIENTATION) {
+ outPointer.orientation = inPointer.absMTOrientation;
+ } else {
+ outPointer.orientation = 0;
+ }
+
+ if (fields & Accumulator::FIELD_ABS_MT_PRESSURE) {
+ outPointer.pressure = inPointer.absMTPressure;
+ } else {
+ // Derive an approximation of pressure.
+ // FIXME Traditionally we have just passed a normalized value based on
+ // ABS_MT_TOUCH_MAJOR as an estimate of pressure but the result is not
+ // very meaningful, particularly on large displays. We should probably let
+ // pressure = touch_major / tool_major but it is unclear whether that will
+ // break applications.
+ outPointer.pressure = outPointer.touchMajor;
+ }
+
+ // Size is an alias for a normalized tool width.
+ // FIXME Normalized tool width doesn't actually make much sense since it literally
+ // means the approaching contact major axis is divided by its full range as
+ // reported by the driver. On a large display this could produce very small values.
+ outPointer.size = outPointer.toolMajor;
if (havePointerIds) {
- if (fields & Accumulator::
- FIELD_ABS_MT_TRACKING_ID) {
- uint32_t id = uint32_t(mAccumulator.pointers[inIndex].absMTTrackingId);
+ if (fields & Accumulator::FIELD_ABS_MT_TRACKING_ID) {
+ uint32_t id = uint32_t(inPointer.absMTTrackingId);
if (id > MAX_POINTER_ID) {
#if DEBUG_POINTERS
@@ -2668,7 +2686,7 @@
havePointerIds = false;
}
else {
- mCurrentTouch.pointers[outCount].id = id;
+ outPointer.id = id;
mCurrentTouch.idToIndex[id] = outCount;
mCurrentTouch.idBits.markBit(id);
}
@@ -2683,6 +2701,8 @@
mCurrentTouch.pointerCount = outCount;
syncTouch(when, havePointerIds);
+
+ mAccumulator.clear();
}
void MultiTouchInputMapper::configureAxes() {
@@ -2697,6 +2717,7 @@
getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MAJOR, & mAxes.toolMajor);
getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MINOR, & mAxes.toolMinor);
getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_ORIENTATION, & mAxes.orientation);
+ getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_PRESSURE, & mAxes.pressure);
if (! mAxes.touchMinor.valid) {
mAxes.touchMinor = mAxes.touchMajor;
@@ -2706,7 +2727,10 @@
mAxes.toolMinor = mAxes.toolMajor;
}
- mAxes.pressure = mAxes.touchMajor;
+ if (! mAxes.pressure.valid) {
+ mAxes.pressure = mAxes.touchMajor;
+ }
+
mAxes.size = mAxes.toolMajor;
}
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 8481d493..94448c1 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -675,7 +675,9 @@
encMeta->setInt32(kKeyChannelCount, mAudioChannels);
encMeta->setInt32(kKeySampleRate, mSampleRate);
encMeta->setInt32(kKeyBitRate, mAudioBitRate);
- encMeta->setInt32(kKeyTimeScale, mAudioTimeScale);
+ if (mAudioTimeScale > 0) {
+ encMeta->setInt32(kKeyTimeScale, mAudioTimeScale);
+ }
OMXClient client;
CHECK_EQ(client.connect(), OK);
@@ -961,7 +963,9 @@
enc_meta->setInt32(kKeyStride, stride);
enc_meta->setInt32(kKeySliceHeight, sliceHeight);
enc_meta->setInt32(kKeyColorFormat, colorFormat);
- enc_meta->setInt32(kKeyTimeScale, mVideoTimeScale);
+ if (mVideoTimeScale > 0) {
+ enc_meta->setInt32(kKeyTimeScale, mVideoTimeScale);
+ }
if (mVideoEncoderProfile != -1) {
enc_meta->setInt32(kKeyVideoProfile, mVideoEncoderProfile);
}
@@ -1041,7 +1045,9 @@
meta->setInt32(kKeyFileType, mOutputFormat);
meta->setInt32(kKeyBitRate, totalBitRate);
meta->setInt32(kKey64BitFileOffset, mUse64BitFileOffset);
- meta->setInt32(kKeyTimeScale, mMovieTimeScale);
+ if (mMovieTimeScale > 0) {
+ meta->setInt32(kKeyTimeScale, mMovieTimeScale);
+ }
if (mTrackEveryTimeDurationUs > 0) {
meta->setInt64(kKeyTrackTimeStatus, mTrackEveryTimeDurationUs);
}
@@ -1117,9 +1123,9 @@
mIFramesIntervalSec = 1;
mAudioSourceNode = 0;
mUse64BitFileOffset = false;
- mMovieTimeScale = 1000;
- mAudioTimeScale = 1000;
- mVideoTimeScale = 1000;
+ mMovieTimeScale = -1;
+ mAudioTimeScale = -1;
+ mVideoTimeScale = -1;
mCameraId = 0;
mVideoEncoderProfile = -1;
mVideoEncoderLevel = -1;
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 6af3a7f..12a1e6e 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -1273,6 +1273,14 @@
hexdump(csd, csd_size);
#endif
+ if (csd_size == 0) {
+ // There's no further information, i.e. no codec specific data
+ // Let's assume that the information provided in the mpeg4 headers
+ // is accurate and hope for the best.
+
+ return OK;
+ }
+
if (csd_size < 2) {
return ERROR_MALFORMED;
}
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 1460f37..f52ec1a 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -72,6 +72,11 @@
bool mIsAudio;
bool mIsMPEG4;
int64_t mTrackDurationUs;
+
+ // For realtime applications, we need to adjust the media clock
+ // for video track based on the audio media clock
+ bool mIsRealTimeRecording;
+ int64_t mMaxTimeStampUs;
int64_t mEstimatedTrackSizeBytes;
int64_t mMaxWriteTimeUs;
int32_t mTimeScale;
@@ -163,6 +168,12 @@
void getCodecSpecificDataFromInputFormatIfPossible();
+ // Determine the track time scale
+ // If it is an audio track, try to use the sampling rate as
+ // the time scale; however, if user chooses the overwrite
+ // value, the user-supplied time scale will be used.
+ void setTimeScale();
+
Track(const Track &);
Track &operator=(const Track &);
};
@@ -429,7 +440,7 @@
mMoovBoxBuffer = (uint8_t *) malloc(mEstimatedMoovBoxSize);
mMoovBoxBufferOffset = 0;
CHECK(mMoovBoxBuffer != NULL);
- int32_t duration = (maxDurationUs * mTimeScale) / 1E6;
+ int32_t duration = (maxDurationUs * mTimeScale + 5E5) / 1E6;
beginBox("moov");
@@ -744,10 +755,6 @@
mReachedEOS(false) {
getCodecSpecificDataFromInputFormatIfPossible();
- if (!mMeta->findInt32(kKeyTimeScale, &mTimeScale)) {
- mTimeScale = 1000;
- }
-
const char *mime;
mMeta->findCString(kKeyMIMEType, &mime);
mIsAvc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
@@ -755,6 +762,28 @@
mIsMPEG4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) ||
!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC);
+ setTimeScale();
+}
+
+void MPEG4Writer::Track::setTimeScale() {
+ LOGV("setTimeScale");
+ // Default time scale
+ mTimeScale = 90000;
+
+ if (mIsAudio) {
+ // Use the sampling rate as the default time scale for audio track.
+ int32_t sampleRate;
+ bool success = mMeta->findInt32(kKeySampleRate, &sampleRate);
+ CHECK(success);
+ mTimeScale = sampleRate;
+ }
+
+ // If someone would like to overwrite the timescale, use user-supplied value.
+ int32_t timeScale;
+ if (mMeta->findInt32(kKeyTimeScale, &timeScale)) {
+ mTimeScale = timeScale;
+ }
+
CHECK(mTimeScale > 0);
}
@@ -940,6 +969,7 @@
mDone = false;
mIsFirstChunk = true;
+ mDriftTimeUs = 0;
for (List<Track *>::iterator it = mTracks.begin();
it != mTracks.end(); ++it) {
ChunkInfo info;
@@ -967,6 +997,14 @@
startTimeUs = 0;
}
+ mIsRealTimeRecording = true;
+ {
+ int32_t isNotRealTime;
+ if (params && params->findInt32(kKeyNotRealTime, &isNotRealTime)) {
+ mIsRealTimeRecording = (isNotRealTime == 0);
+ }
+ }
+
initTrackingProgressStatus(params);
sp<MetaData> meta = new MetaData;
@@ -1322,10 +1360,16 @@
int32_t nZeroLengthFrames = 0;
int64_t lastTimestampUs = 0; // Previous sample time stamp in ms
int64_t lastDurationUs = 0; // Between the previous two samples in ms
+ int64_t currDurationTicks = 0; // Timescale based ticks
+ int64_t lastDurationTicks = 0; // Timescale based ticks
int32_t sampleCount = 1; // Sample count in the current stts table entry
uint32_t previousSampleSize = 0; // Size of the previous sample
int64_t previousPausedDurationUs = 0;
int64_t timestampUs;
+
+ int64_t wallClockTimeUs = 0;
+ int64_t lastWallClockTimeUs = 0;
+
sp<MetaData> meta_data;
bool collectStats = collectStatisticalData();
@@ -1429,6 +1473,33 @@
}
timestampUs -= previousPausedDurationUs;
+ if (mIsRealTimeRecording && !mIsAudio) {
+ // The minor adjustment on the timestamp is heuristic/experimental
+ // We are adjusting the timestamp to reduce the fluctuation of the duration
+ // of neighboring samples. This in turn helps reduce the track header size,
+ // especially, the number of entries in the "stts" box.
+ if (mNumSamples > 1) {
+ int64_t durationUs = timestampUs + mOwner->getDriftTimeUs() - lastTimestampUs;
+ int64_t diffUs = (durationUs > lastDurationUs)
+ ? durationUs - lastDurationUs
+ : lastDurationUs - durationUs;
+ if (diffUs <= 5000) { // XXX: Magic number 5ms
+ timestampUs = lastTimestampUs + lastDurationUs;
+ } else {
+ timestampUs += mOwner->getDriftTimeUs();
+ }
+ }
+ }
+ CHECK(timestampUs >= 0);
+ if (mNumSamples > 1) {
+ if (timestampUs <= lastTimestampUs) {
+ LOGW("Drop a frame, since it arrives too late!");
+ copy->release();
+ copy = NULL;
+ continue;
+ }
+ }
+
LOGV("time stamp: %lld and previous paused duration %lld",
timestampUs, previousPausedDurationUs);
if (timestampUs > mTrackDurationUs) {
@@ -1438,7 +1509,16 @@
mSampleSizes.push_back(sampleSize);
++mNumSamples;
if (mNumSamples > 2) {
- if (lastDurationUs != timestampUs - lastTimestampUs) {
+ // We need to use the time scale based ticks, rather than the
+ // timestamp itself to determine whether we have to use a new
+ // stts entry, since we may have rounding errors.
+ // The calculation is intended to reduce the accumulated
+ // rounding errors.
+ currDurationTicks =
+ ((timestampUs * mTimeScale + 500000LL) / 1000000LL -
+ (lastTimestampUs * mTimeScale + 500000LL) / 1000000LL);
+
+ if (currDurationTicks != lastDurationTicks) {
SttsTableEntry sttsEntry(sampleCount, lastDurationUs);
mSttsTableEntries.push_back(sttsEntry);
sampleCount = 1;
@@ -1453,7 +1533,16 @@
previousSampleSize = sampleSize;
}
lastDurationUs = timestampUs - lastTimestampUs;
+ lastDurationTicks = currDurationTicks;
lastTimestampUs = timestampUs;
+ if (mIsRealTimeRecording && mIsAudio) {
+ wallClockTimeUs = systemTime() / 1000;
+ int64_t wallClockDurationUs = wallClockTimeUs - lastWallClockTimeUs;
+ if (mNumSamples > 2) {
+ mOwner->addDriftTimeUs(lastDurationUs - wallClockDurationUs);
+ }
+ lastWallClockTimeUs = wallClockTimeUs;
+ }
if (isSync != 0) {
mStssTableEntries.push_back(mNumSamples);
@@ -1679,6 +1768,18 @@
}
}
+void MPEG4Writer::addDriftTimeUs(int64_t driftTimeUs) {
+ LOGV("addDriftTimeUs: %lld us", driftTimeUs);
+ Mutex::Autolock autolock(mLock);
+ mDriftTimeUs += driftTimeUs;
+}
+
+int64_t MPEG4Writer::getDriftTimeUs() {
+ LOGV("getDriftTimeUs: %lld us", mDriftTimeUs);
+ Mutex::Autolock autolock(mLock);
+ return mDriftTimeUs;
+}
+
void MPEG4Writer::Track::bufferChunk(int64_t timestampUs) {
LOGV("bufferChunk");
@@ -1709,7 +1810,6 @@
LOGV("%s track time scale: %d",
mIsAudio? "Audio": "Video", mTimeScale);
-
time_t now = time(NULL);
int32_t mvhdTimeScale = mOwner->getTimeScale();
int64_t trakDurationUs = getDurationUs();
@@ -2024,10 +2124,18 @@
mOwner->beginBox("stts");
mOwner->writeInt32(0); // version=0, flags=0
mOwner->writeInt32(mSttsTableEntries.size());
+ int64_t prevTimestampUs = 0;
for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
it != mSttsTableEntries.end(); ++it) {
mOwner->writeInt32(it->sampleCount);
- int32_t dur = (it->sampleDurationUs * mTimeScale + 5E5) / 1E6;
+
+ // Make sure that we are calculating the sample duration the exactly
+ // same way as we made decision on how to create stts entries.
+ int64_t currTimestampUs = prevTimestampUs + it->sampleDurationUs;
+ int32_t dur = ((currTimestampUs * mTimeScale + 500000LL) / 1000000LL -
+ (prevTimestampUs * mTimeScale + 500000LL) / 1000000LL);
+ prevTimestampUs += (it->sampleCount * it->sampleDurationUs);
+
mOwner->writeInt32(dur);
}
mOwner->endBox(); // stts
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index b699d8f..9630092 100644
--- a/media/libstagefright/OggExtractor.cpp
+++ b/media/libstagefright/OggExtractor.cpp
@@ -158,7 +158,7 @@
ReadOptions::SeekMode mode;
if (options && options->getSeekTo(&seekTimeUs, &mode)) {
off_t pos = seekTimeUs * mExtractor->mImpl->approxBitrate() / 8000000ll;
- LOGI("seeking to offset %ld", pos);
+ LOGV("seeking to offset %ld", pos);
if (mExtractor->mImpl->seekToOffset(pos) != OK) {
return ERROR_END_OF_STREAM;
@@ -267,7 +267,7 @@
uint8_t header[27];
if (mSource->readAt(offset, header, sizeof(header))
< (ssize_t)sizeof(header)) {
- LOGE("failed to read %d bytes at offset 0x%08lx", sizeof(header), offset);
+ LOGV("failed to read %d bytes at offset 0x%08lx", sizeof(header), offset);
return ERROR_IO;
}
@@ -384,7 +384,7 @@
packetSize);
if (n < (ssize_t)packetSize) {
- LOGE("failed to read %d bytes at 0x%08lx", packetSize, dataOffset);
+ LOGV("failed to read %d bytes at 0x%08lx", packetSize, dataOffset);
return ERROR_IO;
}
@@ -418,7 +418,7 @@
buffer = NULL;
}
- LOGE("readPage returned %ld", n);
+ LOGV("readPage returned %ld", n);
return n < 0 ? n : (status_t)ERROR_END_OF_STREAM;
}
diff --git a/packages/DefaultContainerService/res/values-cs/strings.xml b/packages/DefaultContainerService/res/values-cs/strings.xml
new file mode 100644
index 0000000..216d715
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-cs/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Package Access Helper"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-da/strings.xml b/packages/DefaultContainerService/res/values-da/strings.xml
new file mode 100644
index 0000000..5243028
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-da/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Hjælp til pakkeadgang"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-de/strings.xml b/packages/DefaultContainerService/res/values-de/strings.xml
new file mode 100644
index 0000000..216d715
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-de/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Package Access Helper"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-el/strings.xml b/packages/DefaultContainerService/res/values-el/strings.xml
new file mode 100644
index 0000000..a4d8144
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-el/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Βοηθός πρόσβασης πακέτου"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-es-rUS/strings.xml b/packages/DefaultContainerService/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..670c2c5
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-es-rUS/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Asist. p/acceder al paq."</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-es/strings.xml b/packages/DefaultContainerService/res/values-es/strings.xml
new file mode 100644
index 0000000..022c461
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-es/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Ayudante acceso a paquete"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-fr/strings.xml b/packages/DefaultContainerService/res/values-fr/strings.xml
new file mode 100644
index 0000000..5c458bc
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-fr/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Aide accès au package"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-it/strings.xml b/packages/DefaultContainerService/res/values-it/strings.xml
new file mode 100644
index 0000000..216d715
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-it/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Package Access Helper"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-ja/strings.xml b/packages/DefaultContainerService/res/values-ja/strings.xml
new file mode 100644
index 0000000..2f57e4e
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-ja/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"パッケージアクセス支援ツール"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-ko/strings.xml b/packages/DefaultContainerService/res/values-ko/strings.xml
new file mode 100644
index 0000000..0304972
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-ko/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"패키지 액세스 도움말"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-nb/strings.xml b/packages/DefaultContainerService/res/values-nb/strings.xml
new file mode 100644
index 0000000..637f54d
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-nb/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Hjelpeprogram for pakketilgang"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-nl/strings.xml b/packages/DefaultContainerService/res/values-nl/strings.xml
new file mode 100644
index 0000000..9ece040
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-nl/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Helper voor pakkettoegang"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-pl/strings.xml b/packages/DefaultContainerService/res/values-pl/strings.xml
new file mode 100644
index 0000000..216d715
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-pl/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Package Access Helper"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-pt-rPT/strings.xml b/packages/DefaultContainerService/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..5c03669
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-pt-rPT/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Ajuda p/ aceder pacotes"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-pt/strings.xml b/packages/DefaultContainerService/res/values-pt/strings.xml
new file mode 100644
index 0000000..5fbd949
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-pt/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Assistente de pacote"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-ru/strings.xml b/packages/DefaultContainerService/res/values-ru/strings.xml
new file mode 100644
index 0000000..ccb0c53
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-ru/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Мастер доступа к пакетам"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-sv/strings.xml b/packages/DefaultContainerService/res/values-sv/strings.xml
new file mode 100644
index 0000000..097a709
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-sv/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Hjälp med paketåtkomst"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-tr/strings.xml b/packages/DefaultContainerService/res/values-tr/strings.xml
new file mode 100644
index 0000000..12ea674
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-tr/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Paket Erişim Yardımcısı"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-zh-rCN/strings.xml b/packages/DefaultContainerService/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..65928b1
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-zh-rCN/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"软件包访问帮助程序"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-zh-rTW/strings.xml b/packages/DefaultContainerService/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..9a43509
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-zh-rTW/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"套件存取輔助程式"</string>
+</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
index 4f39ee4..e828f68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java
@@ -1033,6 +1033,7 @@
iconId = sWifiSignalImages[mLastWifiSignalLevel];
}
+ mService.setIcon("wifi", iconId, 0);
// Show the icon since wi-fi is connected
mService.setIconVisibility("wifi", true);
@@ -1041,11 +1042,11 @@
mIsWifiConnected = false;
iconId = sWifiSignalImages[0];
+ mService.setIcon("wifi", iconId, 0);
// Hide the icon since we're not connected
mService.setIconVisibility("wifi", false);
}
- mService.setIcon("wifi", iconId, 0);
} else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
int iconId;
final int newRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java b/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java
index 07e8653..3a697a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java
@@ -185,11 +185,11 @@
final Segment newSegment = new Segment(n, icon, n.notification.tickerText);
// If there's already a notification schedule for this package and id, remove it.
- for (int i=0; i<initialCount; i++) {
+ for (int i=0; i<mSegments.size(); i++) {
Segment seg = mSegments.get(i);
if (n.id == seg.notification.id && n.pkg.equals(seg.notification.pkg)) {
// just update that one to use this new data instead
- mSegments.remove(i);
+ mSegments.remove(i--); // restart iteration here
}
}
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 509c789..af4d7e4 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -116,6 +116,8 @@
private final LockList mLocks = new LockList();
// some wifi lock statistics
+ private int mFullHighPerfLocksAcquired;
+ private int mFullHighPerfLocksReleased;
private int mFullLocksAcquired;
private int mFullLocksReleased;
private int mScanLocksAcquired;
@@ -1782,8 +1784,8 @@
msg.sendToTarget();
}
- private void sendStartMessage(boolean scanOnlyMode) {
- Message.obtain(mWifiHandler, MESSAGE_START_WIFI, scanOnlyMode ? 1 : 0, 0).sendToTarget();
+ private void sendStartMessage(int lockMode) {
+ Message.obtain(mWifiHandler, MESSAGE_START_WIFI, lockMode, 0).sendToTarget();
}
private void sendAccessPointMessage(boolean enable, WifiConfiguration wifiConfig, int uid) {
@@ -1801,12 +1803,15 @@
boolean wifiEnabled = getPersistedWifiEnabled();
boolean airplaneMode = isAirplaneModeOn() && !mAirplaneModeOverwridden;
boolean lockHeld = mLocks.hasLocks();
- int strongestLockMode;
+ int strongestLockMode = WifiManager.WIFI_MODE_FULL;
boolean wifiShouldBeEnabled = wifiEnabled && !airplaneMode;
boolean wifiShouldBeStarted = !mDeviceIdle || lockHeld;
- if (mDeviceIdle && lockHeld) {
+
+ if (lockHeld) {
strongestLockMode = mLocks.getStrongestLockMode();
- } else {
+ }
+ /* If device is not idle, lockmode cannot be scan only */
+ if (!mDeviceIdle && strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY) {
strongestLockMode = WifiManager.WIFI_MODE_FULL;
}
@@ -1827,7 +1832,7 @@
sWakeLock.acquire();
sendEnableMessage(true, false, mLastEnableUid);
sWakeLock.acquire();
- sendStartMessage(strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY);
+ sendStartMessage(strongestLockMode);
} else if (!mWifiStateTracker.isDriverStopped()) {
int wakeLockTimeout =
Settings.Secure.getInt(
@@ -1905,8 +1910,10 @@
break;
case MESSAGE_START_WIFI:
- mWifiStateTracker.setScanOnlyMode(msg.arg1 != 0);
+ mWifiStateTracker.setScanOnlyMode(msg.arg1 == WifiManager.WIFI_MODE_SCAN_ONLY);
mWifiStateTracker.restart();
+ mWifiStateTracker.setHighPerfMode(msg.arg1 ==
+ WifiManager.WIFI_MODE_FULL_HIGH_PERF);
sWakeLock.release();
break;
@@ -1986,8 +1993,10 @@
}
pw.println();
pw.println("Locks acquired: " + mFullLocksAcquired + " full, " +
+ mFullHighPerfLocksAcquired + " full high perf, " +
mScanLocksAcquired + " scan");
pw.println("Locks released: " + mFullLocksReleased + " full, " +
+ mFullHighPerfLocksReleased + " full high perf, " +
mScanLocksReleased + " scan");
pw.println();
pw.println("Locks held:");
@@ -2042,11 +2051,15 @@
if (mList.isEmpty()) {
return WifiManager.WIFI_MODE_FULL;
}
- for (WifiLock l : mList) {
- if (l.mMode == WifiManager.WIFI_MODE_FULL) {
- return WifiManager.WIFI_MODE_FULL;
- }
+
+ if (mFullHighPerfLocksAcquired > mFullHighPerfLocksReleased) {
+ return WifiManager.WIFI_MODE_FULL_HIGH_PERF;
}
+
+ if (mFullLocksAcquired > mFullLocksReleased) {
+ return WifiManager.WIFI_MODE_FULL;
+ }
+
return WifiManager.WIFI_MODE_SCAN_ONLY;
}
@@ -2085,7 +2098,11 @@
public boolean acquireWifiLock(IBinder binder, int lockMode, String tag) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
- if (lockMode != WifiManager.WIFI_MODE_FULL && lockMode != WifiManager.WIFI_MODE_SCAN_ONLY) {
+ if (lockMode != WifiManager.WIFI_MODE_FULL &&
+ lockMode != WifiManager.WIFI_MODE_SCAN_ONLY &&
+ lockMode != WifiManager.WIFI_MODE_FULL_HIGH_PERF) {
+ Slog.e(TAG, "Illegal argument, lockMode= " + lockMode);
+ if (DBG) throw new IllegalArgumentException("lockMode=" + lockMode);
return false;
}
WifiLock wifiLock = new WifiLock(lockMode, tag, binder);
@@ -2107,6 +2124,12 @@
++mFullLocksAcquired;
mBatteryStats.noteFullWifiLockAcquired(uid);
break;
+ case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
+ ++mFullHighPerfLocksAcquired;
+ /* Treat high power as a full lock for battery stats */
+ mBatteryStats.noteFullWifiLockAcquired(uid);
+ break;
+
case WifiManager.WIFI_MODE_SCAN_ONLY:
++mScanLocksAcquired;
mBatteryStats.noteScanWifiLockAcquired(uid);
@@ -2146,6 +2169,10 @@
++mFullLocksReleased;
mBatteryStats.noteFullWifiLockReleased(uid);
break;
+ case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
+ ++mFullHighPerfLocksReleased;
+ mBatteryStats.noteFullWifiLockReleased(uid);
+ break;
case WifiManager.WIFI_MODE_SCAN_ONLY:
++mScanLocksReleased;
mBatteryStats.noteScanWifiLockReleased(uid);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index e5d1025..4762ddb 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -17,7 +17,6 @@
package com.android.server.am;
import com.android.internal.R;
-import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.os.BatteryStatsImpl;
import com.android.server.AttributeCache;
import com.android.server.IntentResolver;
@@ -39,7 +38,6 @@
import android.app.ApplicationErrorReport;
import android.app.Dialog;
import android.app.IActivityController;
-import android.app.IActivityManager;
import android.app.IActivityWatcher;
import android.app.IApplicationThread;
import android.app.IInstrumentationWatcher;
@@ -50,7 +48,6 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.app.ResultInfo;
import android.app.Service;
import android.app.backup.IBackupManager;
import android.content.ActivityNotFoundException;
@@ -94,7 +91,6 @@
import android.os.Message;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
-import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -109,6 +105,7 @@
import android.util.Log;
import android.util.PrintWriterPrinter;
import android.util.SparseArray;
+import android.util.TimeUtils;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -1185,10 +1182,10 @@
case CHECK_EXCESSIVE_WAKE_LOCKS_MSG: {
synchronized (ActivityManagerService.this) {
checkExcessiveWakeLocksLocked(true);
- mHandler.removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+ removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
if (mSleeping) {
- Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
- mHandler.sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY);
+ Message nmsg = obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
+ sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY);
}
}
} break;
@@ -11304,7 +11301,7 @@
final long curRealtime = SystemClock.elapsedRealtime();
final long timeSince = curRealtime - mLastWakeLockCheckTime;
mLastWakeLockCheckTime = curRealtime;
- if (timeSince < 5*60*1000) {
+ if (timeSince < (WAKE_LOCK_CHECK_DELAY/3)) {
doKills = false;
}
int i = mLruProcesses.size();
@@ -11318,9 +11315,19 @@
app.pid, curRealtime);
}
long timeUsed = wtime - app.lastWakeTime;
- Slog.i(TAG, "Wake for " + app + ": over "
- + timeSince + " used " + timeUsed
- + " (" + ((timeUsed*100)/timeSince) + "%)");
+ if (false) {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("Wake for ");
+ app.toShortString(sb);
+ sb.append(": over ");
+ TimeUtils.formatDuration(timeSince, sb);
+ sb.append(" used ");
+ TimeUtils.formatDuration(timeUsed, sb);
+ sb.append(" (");
+ sb.append((timeUsed*100)/timeSince);
+ sb.append("%)");
+ Slog.i(TAG, sb.toString());
+ }
// If a process has held a wake lock for more
// than 50% of the time during this period,
// that sounds pad. Kill!
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 73e489f..6d1fbab 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -17,7 +17,6 @@
package com.android.server.am;
import com.android.internal.os.BatteryStatsImpl;
-import com.android.server.Watchdog;
import android.app.ActivityManager;
import android.app.Dialog;
@@ -27,8 +26,9 @@
import android.content.pm.ApplicationInfo;
import android.os.Bundle;
import android.os.IBinder;
-import android.os.RemoteException;
+import android.os.SystemClock;
import android.util.PrintWriterPrinter;
+import android.util.TimeUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -129,6 +129,8 @@
ComponentName errorReportReceiver;
void dump(PrintWriter pw, String prefix) {
+ final long now = SystemClock.uptimeMillis();
+
if (info.className != null) {
pw.print(prefix); pw.print("class="); pw.println(info.className);
}
@@ -158,7 +160,8 @@
pw.print(" curReceiver="); pw.println(curReceiver);
pw.print(prefix); pw.print("pid="); pw.print(pid); pw.print(" starting=");
pw.print(starting); pw.print(" lastPss="); pw.println(lastPss);
- pw.print(prefix); pw.print("lastActivityTime="); pw.print(lastActivityTime);
+ pw.print(prefix); pw.print("lastActivityTime=");
+ TimeUtils.formatDuration(lastActivityTime, now, pw);
pw.print(" lruWeight="); pw.print(lruWeight);
pw.print(" hidden="); pw.print(hidden);
pw.print(" empty="); pw.println(empty);
@@ -179,8 +182,10 @@
pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq);
pw.print(" lruSeq="); pw.println(lruSeq);
pw.print(prefix); pw.print("lastWakeTime="); pw.print(lastWakeTime);
- pw.print(" lastRequestedGc="); pw.print(lastRequestedGc);
- pw.print(" lastLowMemory="); pw.print(lastLowMemory);
+ pw.print(" lastRequestedGc=");
+ TimeUtils.formatDuration(lastRequestedGc, now, pw);
+ pw.print(" lastLowMemory=");
+ TimeUtils.formatDuration(lastLowMemory, now, pw);
pw.print(" reportLowMemory="); pw.println(reportLowMemory);
if (killedBackground) {
pw.print(prefix); pw.print("killedBackground="); pw.println(killedBackground);
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 35f18750..ab5a78d 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -31,6 +31,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Slog;
+import android.util.TimeUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -117,7 +118,10 @@
StartItem si = list.get(i);
pw.print(prefix); pw.print("#"); pw.print(i);
pw.print(" id="); pw.print(si.id);
- if (now != 0) pw.print(" dur="); pw.print(now-si.deliveredTime);
+ if (now != 0) {
+ pw.print(" dur=");
+ TimeUtils.formatDuration(si.deliveredTime, now, pw);
+ }
if (si.deliveryCount != 0) {
pw.print(" dc="); pw.print(si.deliveryCount);
}
@@ -140,9 +144,10 @@
pw.print(prefix); pw.print("permission="); pw.println(permission);
}
long now = SystemClock.uptimeMillis();
- pw.print(prefix); pw.print("baseDir="); pw.print(baseDir);
- if (!resDir.equals(baseDir)) pw.print(" resDir="); pw.print(resDir);
- pw.print(" dataDir="); pw.println(dataDir);
+ long nowReal = SystemClock.elapsedRealtime();
+ pw.print(prefix); pw.print("baseDir="); pw.println(baseDir);
+ if (!resDir.equals(baseDir)) pw.print(prefix); pw.print("resDir="); pw.println(resDir);
+ pw.print(prefix); pw.print("dataDir="); pw.println(dataDir);
pw.print(prefix); pw.print("app="); pw.println(app);
if (isForeground || foregroundId != 0) {
pw.print(prefix); pw.print("isForeground="); pw.print(isForeground);
@@ -150,10 +155,15 @@
pw.print(" foregroundNoti="); pw.println(foregroundNoti);
}
pw.print(prefix); pw.print("createTime=");
- pw.print(createTime-SystemClock.elapsedRealtime());
- pw.print(" lastActivity="); pw.print(lastActivity-now);
- pw.print(" executingStart="); pw.print(executingStart-now);
- pw.print(" restartTime="); pw.println(restartTime);
+ TimeUtils.formatDuration(createTime, nowReal, pw);
+ pw.print(" lastActivity=");
+ TimeUtils.formatDuration(lastActivity, now, pw);
+ pw.println("");
+ pw.print(prefix); pw.print(" executingStart=");
+ TimeUtils.formatDuration(executingStart, now, pw);
+ pw.print(" restartTime=");
+ TimeUtils.formatDuration(restartTime, now, pw);
+ pw.println("");
if (startRequested || lastStartId != 0) {
pw.print(prefix); pw.print("startRequested="); pw.print(startRequested);
pw.print(" stopIfKilled="); pw.print(stopIfKilled);
@@ -164,13 +174,15 @@
|| restartDelay != 0 || nextRestartTime != 0) {
pw.print(prefix); pw.print("executeNesting="); pw.print(executeNesting);
pw.print(" restartCount="); pw.print(restartCount);
- pw.print(" restartDelay="); pw.print(restartDelay-now);
- pw.print(" nextRestartTime="); pw.print(nextRestartTime-now);
+ pw.print(" restartDelay=");
+ TimeUtils.formatDuration(restartDelay, now, pw);
+ pw.print(" nextRestartTime=");
+ TimeUtils.formatDuration(nextRestartTime, now, pw);
pw.print(" crashCount="); pw.println(crashCount);
}
if (deliveredStarts.size() > 0) {
pw.print(prefix); pw.println("Delivered Starts:");
- dumpStartList(pw, prefix, deliveredStarts, SystemClock.uptimeMillis());
+ dumpStartList(pw, prefix, deliveredStarts, now);
}
if (pendingStarts.size() > 0) {
pw.print(prefix); pw.println("Pending Starts:");
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index ea6aa94..c1165c7 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -197,6 +197,9 @@
// capabilities of the GPS engine
private int mEngineCapabilities;
+ // true if XTRA is supported
+ private boolean mSupportsXtra;
+
// for calculating time to first fix
private long mFixRequestTime = 0;
// time to first fix for most recent session
@@ -635,6 +638,7 @@
mEnabled = native_init();
if (mEnabled) {
+ mSupportsXtra = native_supports_xtra();
if (mSuplServerHost != null) {
native_set_agps_server(AGPS_TYPE_SUPL, mSuplServerHost, mSuplServerPort);
}
@@ -839,7 +843,7 @@
sendMessage(INJECT_NTP_TIME, 0, null);
result = true;
} else if ("force_xtra_injection".equals(command)) {
- if (native_supports_xtra()) {
+ if (mSupportsXtra) {
xtraDownloadRequest();
result = true;
}
@@ -1372,7 +1376,7 @@
handleInjectNtpTime();
break;
case DOWNLOAD_XTRA_DATA:
- if (native_supports_xtra()) {
+ if (mSupportsXtra) {
handleDownloadXtraData();
}
break;
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index ebe71ab..ba58b43 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -319,9 +319,9 @@
bool isScreenOn();
bool isScreenBright();
- // Weak references to all currently registered input channels by receive fd.
+ // Weak references to all currently registered input channels by connection pointer.
Mutex mInputChannelRegistryLock;
- KeyedVector<int, jweak> mInputChannelObjWeakByReceiveFd;
+ KeyedVector<InputChannel*, jweak> mInputChannelObjWeakTable;
jobject getInputChannelObjLocal(JNIEnv* env, const sp<InputChannel>& inputChannel);
@@ -509,8 +509,7 @@
{
AutoMutex _l(mInputChannelRegistryLock);
- ssize_t index = mInputChannelObjWeakByReceiveFd.indexOfKey(
- inputChannel->getReceivePipeFd());
+ ssize_t index = mInputChannelObjWeakTable.indexOfKey(inputChannel.get());
if (index >= 0) {
LOGE("Input channel object '%s' has already been registered",
inputChannel->getName().string());
@@ -518,8 +517,7 @@
goto DeleteWeakRef;
}
- mInputChannelObjWeakByReceiveFd.add(inputChannel->getReceivePipeFd(),
- inputChannelObjWeak);
+ mInputChannelObjWeakTable.add(inputChannel.get(), inputChannelObjWeak);
}
status = mInputManager->registerInputChannel(inputChannel);
@@ -534,7 +532,7 @@
// Failed!
{
AutoMutex _l(mInputChannelRegistryLock);
- mInputChannelObjWeakByReceiveFd.removeItem(inputChannel->getReceivePipeFd());
+ mInputChannelObjWeakTable.removeItem(inputChannel.get());
}
DeleteWeakRef:
@@ -548,16 +546,15 @@
{
AutoMutex _l(mInputChannelRegistryLock);
- ssize_t index = mInputChannelObjWeakByReceiveFd.indexOfKey(
- inputChannel->getReceivePipeFd());
+ ssize_t index = mInputChannelObjWeakTable.indexOfKey(inputChannel.get());
if (index < 0) {
LOGE("Input channel object '%s' is not currently registered",
inputChannel->getName().string());
return INVALID_OPERATION;
}
- inputChannelObjWeak = mInputChannelObjWeakByReceiveFd.valueAt(index);
- mInputChannelObjWeakByReceiveFd.removeItemsAt(index);
+ inputChannelObjWeak = mInputChannelObjWeakTable.valueAt(index);
+ mInputChannelObjWeakTable.removeItemsAt(index);
}
env->DeleteWeakGlobalRef(inputChannelObjWeak);
@@ -572,13 +569,12 @@
{
AutoMutex _l(mInputChannelRegistryLock);
- ssize_t index = mInputChannelObjWeakByReceiveFd.indexOfKey(
- inputChannel->getReceivePipeFd());
+ ssize_t index = mInputChannelObjWeakTable.indexOfKey(inputChannel.get());
if (index < 0) {
return NULL;
}
- jweak inputChannelObjWeak = mInputChannelObjWeakByReceiveFd.valueAt(index);
+ jweak inputChannelObjWeak = mInputChannelObjWeakTable.valueAt(index);
return env->NewLocalRef(inputChannelObjWeak);
}
}
diff --git a/services/jni/com_android_server_PowerManagerService.cpp b/services/jni/com_android_server_PowerManagerService.cpp
index b80dbc5..146c177 100644
--- a/services/jni/com_android_server_PowerManagerService.cpp
+++ b/services/jni/com_android_server_PowerManagerService.cpp
@@ -22,6 +22,7 @@
#include "jni.h"
#include <limits.h>
#include <android_runtime/AndroidRuntime.h>
+#include <utils/Timers.h>
#include "com_android_server_PowerManagerService.h"
namespace android {
@@ -43,6 +44,11 @@
static bool gScreenOn;
static bool gScreenBright;
+static nsecs_t gLastEventTime[POWER_MANAGER_LAST_EVENT + 1];
+
+// Throttling interval for user activity calls.
+static const nsecs_t MIN_TIME_BETWEEN_USERACTIVITIES = 500 * 1000000L; // 500ms
+
// ----------------------------------------------------------------------------
static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
@@ -67,6 +73,21 @@
void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType) {
if (gPowerManagerServiceObj) {
+ // Throttle calls into user activity by event type.
+ // We're a little conservative about argument checking here in case the caller
+ // passes in bad data which could corrupt system state.
+ if (eventType >= 0 && eventType <= POWER_MANAGER_LAST_EVENT) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ if (eventTime > now) {
+ eventTime = now;
+ }
+
+ if (gLastEventTime[eventType] + MIN_TIME_BETWEEN_USERACTIVITIES > eventTime) {
+ return;
+ }
+ gLastEventTime[eventType] = eventTime;
+ }
+
JNIEnv* env = AndroidRuntime::getJNIEnv();
env->CallVoidMethod(gPowerManagerServiceObj, gPowerManagerServiceClassInfo.userActivity,
@@ -136,6 +157,12 @@
GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivity, gPowerManagerServiceClassInfo.clazz,
"userActivity", "(JZIZ)V");
+ // Initialize
+ for (int i = 0; i < POWER_MANAGER_LAST_EVENT; i++) {
+ gLastEventTime[i] = LLONG_MIN;
+ }
+ gScreenOn = true;
+ gScreenBright = true;
return 0;
}
diff --git a/services/jni/com_android_server_PowerManagerService.h b/services/jni/com_android_server_PowerManagerService.h
index 9b05f38..7c329b2 100644
--- a/services/jni/com_android_server_PowerManagerService.h
+++ b/services/jni/com_android_server_PowerManagerService.h
@@ -30,6 +30,8 @@
POWER_MANAGER_LONG_TOUCH_EVENT = 3,
POWER_MANAGER_TOUCH_UP_EVENT = 4,
POWER_MANAGER_BUTTON_EVENT = 5, // Button and trackball events.
+
+ POWER_MANAGER_LAST_EVENT = POWER_MANAGER_BUTTON_EVENT, // Last valid event code.
};
extern bool android_server_PowerManagerService_isScreenOn();
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 79772ed..a14bfb5 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -5,7 +5,6 @@
clz.cpp.arm \
DisplayHardware/DisplayHardware.cpp \
DisplayHardware/DisplayHardwareBase.cpp \
- DisplayHardware/HWComposer.cpp \
BlurFilter.cpp.arm \
GLExtensions.cpp \
Layer.cpp \
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index 166c528..2eac0a8 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -36,11 +36,11 @@
#include "DisplayHardware/DisplayHardware.h"
+#include <hardware/copybit.h>
#include <hardware/overlay.h>
#include <hardware/gralloc.h>
#include "GLExtensions.h"
-#include "HWComposer.h"
using namespace android;
@@ -76,7 +76,7 @@
const sp<SurfaceFlinger>& flinger,
uint32_t dpy)
: DisplayHardwareBase(flinger, dpy),
- mFlags(0), mHwc(0)
+ mFlags(0)
{
init(dpy);
}
@@ -262,17 +262,6 @@
// Unbind the context from this thread
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-
-
- // initialize the H/W composer
- mHwc = new HWComposer();
- if (mHwc->initCheck() == NO_ERROR) {
- mHwc->setFrameBuffer(mDisplay, mSurface);
- }
-}
-
-HWComposer& DisplayHardware::getHwComposer() const {
- return *mHwc;
}
/*
@@ -328,12 +317,7 @@
}
mPageFlipCount++;
-
- if (mHwc->initCheck() == NO_ERROR) {
- mHwc->commit();
- } else {
- eglSwapBuffers(dpy, surface);
- }
+ eglSwapBuffers(dpy, surface);
checkEGLErrors("eglSwapBuffers");
// for debugging
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.h b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
index f2cfd2d..66bf521 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
@@ -34,11 +34,12 @@
#include "DisplayHardware/DisplayHardwareBase.h"
struct overlay_control_device_t;
+struct framebuffer_device_t;
+struct copybit_image_t;
namespace android {
class FramebufferNativeWindow;
-class HWComposer;
class DisplayHardware : public DisplayHardwareBase
{
@@ -79,9 +80,6 @@
uint32_t getPageFlipCount() const;
EGLDisplay getEGLDisplay() const { return mDisplay; }
overlay_control_device_t* getOverlayEngine() const { return mOverlayEngine; }
-
- // Hardware Composer
- HWComposer& getHwComposer() const;
status_t compositionComplete() const;
@@ -109,8 +107,6 @@
GLint mMaxViewportDims;
GLint mMaxTextureSize;
- HWComposer* mHwc;
-
sp<FramebufferNativeWindow> mNativeWindow;
overlay_control_device_t* mOverlayEngine;
};
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
deleted file mode 100644
index 0291d78..0000000
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-
-#include <hardware/hardware.h>
-
-#include <cutils/log.h>
-
-#include <EGL/egl.h>
-
-#include "HWComposer.h"
-
-namespace android {
-// ---------------------------------------------------------------------------
-
-HWComposer::HWComposer()
- : mModule(0), mHwc(0), mList(0), mCapacity(0),
- mDpy(EGL_NO_DISPLAY), mSur(EGL_NO_SURFACE)
-{
- int err = hw_get_module(HWC_HARDWARE_MODULE_ID, &mModule);
- LOGW_IF(err, "%s module not found", HWC_HARDWARE_MODULE_ID);
- if (err == 0) {
- err = hwc_open(mModule, &mHwc);
- LOGE_IF(err, "%s device failed to initialize (%s)",
- HWC_HARDWARE_COMPOSER, strerror(-err));
- }
-}
-
-HWComposer::~HWComposer() {
- free(mList);
- if (mHwc) {
- hwc_close(mHwc);
- }
-}
-
-status_t HWComposer::initCheck() const {
- return mHwc ? NO_ERROR : NO_INIT;
-}
-
-void HWComposer::setFrameBuffer(EGLDisplay dpy, EGLSurface sur) {
- mDpy = (hwc_display_t)dpy;
- mSur = (hwc_surface_t)sur;
-}
-
-status_t HWComposer::createWorkList(size_t numLayers) {
- if (mHwc) {
- if (!mList || mCapacity < numLayers) {
- free(mList);
- size_t size = sizeof(hwc_layer_list) + numLayers*sizeof(hwc_layer_t);
- mList = (hwc_layer_list_t*)malloc(size);
- mCapacity = numLayers;
- }
- mList->flags = HWC_GEOMETRY_CHANGED;
- mList->numHwLayers = numLayers;
- }
- return NO_ERROR;
-}
-
-status_t HWComposer::prepare() const {
- int err = mHwc->prepare(mHwc, mList);
- return (status_t)err;
-}
-
-status_t HWComposer::commit() const {
- int err = mHwc->set(mHwc, mDpy, mSur, mList);
- mList->flags &= ~HWC_GEOMETRY_CHANGED;
- return (status_t)err;
-}
-
-size_t HWComposer::getNumLayers() const {
- return mList ? mList->numHwLayers : 0;
-}
-
-hwc_layer_t* HWComposer::getLayers() const {
- return mList ? mList->hwLayers : 0;
-}
-
-// ---------------------------------------------------------------------------
-}; // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
deleted file mode 100644
index c5d5c2b..0000000
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SF_HWCOMPOSER_H
-#define ANDROID_SF_HWCOMPOSER_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <EGL/egl.h>
-
-#include <hardware/hwcomposer.h>
-
-namespace android {
-// ---------------------------------------------------------------------------
-
-class HWComposer
-{
-public:
-
- HWComposer();
- ~HWComposer();
-
- status_t initCheck() const;
-
- // tells the HAL what the framebuffer is
- void setFrameBuffer(EGLDisplay dpy, EGLSurface sur);
-
- // create a work list for numLayers layer
- status_t createWorkList(size_t numLayers);
-
- // Asks the HAL what it can do
- status_t prepare() const;
-
- // commits the list
- status_t commit() const;
-
-
- size_t getNumLayers() const;
- hwc_layer_t* getLayers() const;
-
-private:
- hw_module_t const* mModule;
- hwc_composer_device_t* mHwc;
- hwc_layer_list_t* mList;
- size_t mCapacity;
- hwc_display_t mDpy;
- hwc_surface_t mSur;
-};
-
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_SF_HWCOMPOSER_H
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 3720e1669..629d993 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -35,7 +35,6 @@
#include "Layer.h"
#include "SurfaceFlinger.h"
#include "DisplayHardware/DisplayHardware.h"
-#include "DisplayHardware/HWComposer.h"
#define DEBUG_RESIZE 0
@@ -178,62 +177,6 @@
return NO_ERROR;
}
-void Layer::setGeometry(hwc_layer_t* hwcl)
-{
- hwcl->compositionType = HWC_FRAMEBUFFER;
- hwcl->hints = 0;
- hwcl->flags = 0;
- hwcl->transform = 0;
- hwcl->blending = HWC_BLENDING_NONE;
-
- // we can't do alpha-fade with the hwc HAL
- const State& s(drawingState());
- if (s.alpha < 0xFF) {
- hwcl->flags = HWC_SKIP_LAYER;
- return;
- }
-
- // we can only handle simple transformation
- if (mOrientation & Transform::ROT_INVALID) {
- hwcl->flags = HWC_SKIP_LAYER;
- return;
- }
-
- hwcl->transform = mOrientation;
-
- if (needsBlending()) {
- hwcl->blending = mPremultipliedAlpha ?
- HWC_BLENDING_PREMULT : HWC_BLENDING_COVERAGE;
- }
-
- hwcl->displayFrame.left = mTransformedBounds.left;
- hwcl->displayFrame.top = mTransformedBounds.top;
- hwcl->displayFrame.right = mTransformedBounds.right;
- hwcl->displayFrame.bottom = mTransformedBounds.bottom;
-
- hwcl->visibleRegionScreen.rects =
- reinterpret_cast<hwc_rect_t const *>(
- visibleRegionScreen.getArray(
- &hwcl->visibleRegionScreen.numRects));
-}
-
-void Layer::setPerFrameData(hwc_layer_t* hwcl) {
- sp<GraphicBuffer> buffer(mBufferManager.getActiveBuffer());
- if (buffer == NULL) {
- // this situation can happen if we ran out of memory for instance.
- // not much we can do. continue to use whatever texture was bound
- // to this context.
- hwcl->handle = NULL;
- return;
- }
- hwcl->handle = const_cast<native_handle_t*>(buffer->handle);
- // TODO: set the crop value properly
- hwcl->sourceCrop.left = 0;
- hwcl->sourceCrop.top = 0;
- hwcl->sourceCrop.right = buffer->width;
- hwcl->sourceCrop.bottom = buffer->height;
-}
-
void Layer::reloadTexture(const Region& dirty)
{
sp<GraphicBuffer> buffer(mBufferManager.getActiveBuffer());
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 188da6a..e1d283b 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -68,8 +68,6 @@
bool isFixedSize() const;
// LayerBase interface
- virtual void setGeometry(hwc_layer_t* hwcl);
- virtual void setPerFrameData(hwc_layer_t* hwcl);
virtual void onDraw(const Region& clip) const;
virtual uint32_t doTransaction(uint32_t transactionFlags);
virtual void lockPageFlip(bool& recomputeVisibleRegions);
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index 043d54d..91ac915 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -307,15 +307,6 @@
}
}
-void LayerBase::setGeometry(hwc_layer_t* hwcl) {
- hwcl->flags |= HWC_SKIP_LAYER;
-}
-
-void LayerBase::setPerFrameData(hwc_layer_t* hwcl) {
- hwcl->compositionType = HWC_FRAMEBUFFER;
- hwcl->handle = NULL;
-}
-
void LayerBase::draw(const Region& clip) const
{
// reset GL state
diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
index dd1cd05..22bf857 100644
--- a/services/surfaceflinger/LayerBase.h
+++ b/services/surfaceflinger/LayerBase.h
@@ -35,8 +35,6 @@
#include <pixelflinger/pixelflinger.h>
-#include <hardware/hwcomposer.h>
-
#include "Transform.h"
namespace android {
@@ -110,10 +108,6 @@
virtual const char* getTypeId() const { return "LayerBase"; }
- virtual void setGeometry(hwc_layer_t* hwcl);
-
- virtual void setPerFrameData(hwc_layer_t* hwcl);
-
/**
* draw - performs some global clipping optimizations
* and calls onDraw().
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index d257897..637ae48 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -52,7 +52,6 @@
#include "SurfaceFlinger.h"
#include "DisplayHardware/DisplayHardware.h"
-#include "DisplayHardware/HWComposer.h"
/* ideally AID_GRAPHICS would be in a semi-public header
* or there would be a way to map a user/group name to its id
@@ -77,7 +76,6 @@
mAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER"),
mDump("android.permission.DUMP"),
mVisibleRegionsDirty(false),
- mHwWorkListDirty(false),
mDeferReleaseConsole(false),
mFreezeDisplay(false),
mFreezeCount(0),
@@ -370,11 +368,6 @@
// post surfaces (if needed)
handlePageFlip();
- if (UNLIKELY(mHwWorkListDirty)) {
- // build the h/w work list
- handleWorkList();
- }
-
const DisplayHardware& hw(graphicPlane(0).displayHardware());
if (LIKELY(hw.canDraw() && !isFrozen())) {
// repaint the framebuffer (if needed)
@@ -450,7 +443,6 @@
handleTransactionLocked(transactionFlags, ditchedLayers);
mLastTransactionTime = systemTime() - now;
mDebugInTransaction = 0;
- mHwWorkListDirty = true;
// here the transaction has been committed
}
@@ -458,7 +450,6 @@
* Clean-up all layers that went away
* (do this without the lock held)
*/
-
const size_t count = ditchedLayers.size();
for (size_t i=0 ; i<count ; i++) {
if (ditchedLayers[i] != 0) {
@@ -692,8 +683,8 @@
void SurfaceFlinger::handlePageFlip()
{
bool visibleRegions = mVisibleRegionsDirty;
- LayerVector& currentLayers(
- const_cast<LayerVector&>(mDrawingState.layersSortedByZ));
+ LayerVector& currentLayers = const_cast<LayerVector&>(
+ mDrawingState.layersSortedByZ);
visibleRegions |= lockPageFlip(currentLayers);
const DisplayHardware& hw = graphicPlane(0).displayHardware();
@@ -716,7 +707,6 @@
mWormholeRegion = screenRegion.subtract(opaqueRegion);
mVisibleRegionsDirty = false;
- mHwWorkListDirty = true;
}
unlockPageFlip(currentLayers);
@@ -747,20 +737,6 @@
}
}
-void SurfaceFlinger::handleWorkList()
-{
- mHwWorkListDirty = false;
- HWComposer& hwc(graphicPlane(0).displayHardware().getHwComposer());
- if (hwc.initCheck() == NO_ERROR) {
- const Vector< sp<LayerBase> >& currentLayers(mVisibleLayersSortedByZ);
- const size_t count = currentLayers.size();
- hwc.createWorkList(count);
- hwc_layer_t* const cur(hwc.getLayers());
- for (size_t i=0 ; cur && i<count ; i++) {
- currentLayers[i]->setGeometry(&cur[i]);
- }
- }
-}
void SurfaceFlinger::handleRepaint()
{
@@ -825,72 +801,9 @@
// draw something...
drawWormhole();
}
-
- status_t err = NO_ERROR;
const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
- size_t count = layers.size();
-
- const DisplayHardware& hw(graphicPlane(0).displayHardware());
- HWComposer& hwc(hw.getHwComposer());
- hwc_layer_t* const cur(hwc.getLayers());
-
- LOGE_IF(cur && hwc.getNumLayers() != count,
- "HAL number of layers (%d) doesn't match surfaceflinger (%d)",
- hwc.getNumLayers(), count);
-
- // just to be extra-safe, use the smallest count
- if (hwc.initCheck() == NO_ERROR) {
- count = count < hwc.getNumLayers() ? count : hwc.getNumLayers();
- }
-
- /*
- * update the per-frame h/w composer data for each layer
- * and build the transparent region of the FB
- */
- Region transparent;
- if (cur) {
- for (size_t i=0 ; i<count ; i++) {
- const sp<LayerBase>& layer(layers[i]);
- layer->setPerFrameData(&cur[i]);
- if (cur[i].hints & HWC_HINT_CLEAR_FB) {
- if (!(layer->needsBlending())) {
- transparent.orSelf(layer->visibleRegionScreen);
- }
- }
- }
- err = hwc.prepare();
- LOGE_IF(err, "HWComposer::prepare failed (%s)", strerror(-err));
- }
-
- /*
- * clear the area of the FB that need to be transparent
- */
- transparent.andSelf(dirty);
- if (!transparent.isEmpty()) {
- glClearColor(0,0,0,0);
- Region::const_iterator it = transparent.begin();
- Region::const_iterator const end = transparent.end();
- const int32_t height = hw.getHeight();
- while (it != end) {
- const Rect& r(*it++);
- const GLint sy = height - (r.top + r.height());
- glScissor(r.left, sy, r.width(), r.height());
- glClear(GL_COLOR_BUFFER_BIT);
- }
- }
-
-
- /*
- * and then, render the layers targeted at the framebuffer
- */
- for (size_t i=0 ; i<count ; i++) {
- if (cur) {
- if (!(cur[i].compositionType == HWC_FRAMEBUFFER) ||
- cur[i].flags & HWC_SKIP_LAYER) {
- // skip layers handled by the HAL
- continue;
- }
- }
+ const size_t count = layers.size();
+ for (size_t i=0 ; i<count ; ++i) {
const sp<LayerBase>& layer(layers[i]);
const Region clip(dirty.intersect(layer->visibleRegionScreen));
if (!clip.isEmpty()) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8e286e5..8ecfc01 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -296,7 +296,6 @@
void handlePageFlip();
bool lockPageFlip(const LayerVector& currentLayers);
void unlockPageFlip(const LayerVector& currentLayers);
- void handleWorkList();
void handleRepaint();
void postFramebuffer();
void composeSurfaces(const Region& dirty);
@@ -371,7 +370,6 @@
Region mInvalidRegion;
Region mWormholeRegion;
bool mVisibleRegionsDirty;
- bool mHwWorkListDirty;
bool mDeferReleaseConsole;
bool mFreezeDisplay;
int32_t mFreezeCount;
diff --git a/telephony/java/com/android/internal/telephony/CallManager.java b/telephony/java/com/android/internal/telephony/CallManager.java
index 819cfbe..8e08592 100644
--- a/telephony/java/com/android/internal/telephony/CallManager.java
+++ b/telephony/java/com/android/internal/telephony/CallManager.java
@@ -16,16 +16,17 @@
package com.android.internal.telephony;
+import com.android.internal.telephony.sip.SipPhone;
+
import android.content.Context;
+import android.media.AudioManager;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Message;
import android.os.RegistrantList;
-import android.util.Log;
-
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
-
+import android.util.Log;
import java.util.ArrayList;
import java.util.Collections;
@@ -52,7 +53,7 @@
*/
public final class CallManager {
- private static final String LOG_TAG ="GSM";
+ private static final String LOG_TAG ="Phone";
private static final boolean LOCAL_DEBUG = true;
private static final int EVENT_DISCONNECT = 100;
@@ -303,6 +304,32 @@
}
}
+ public void setAudioMode() {
+ Context context = getContext();
+ if (context == null) return;
+ AudioManager audioManager = (AudioManager)
+ context.getSystemService(Context.AUDIO_SERVICE);
+
+ int mode = AudioManager.MODE_NORMAL;
+ switch (getState()) {
+ case RINGING:
+ mode = AudioManager.MODE_RINGTONE;
+ break;
+ case OFFHOOK:
+ Phone fgPhone = getFgPhone();
+ if (!(fgPhone instanceof SipPhone)) {
+ mode = AudioManager.MODE_IN_CALL;
+ }
+ break;
+ }
+ audioManager.setMode(mode);
+ }
+
+ private Context getContext() {
+ Phone defaultPhone = getDefaultPhone();
+ return ((defaultPhone == null) ? null : defaultPhone.getContext());
+ }
+
private void registerForPhoneStates(Phone phone) {
phone.registerForPreciseCallStateChanged(mHandler, EVENT_PRECISE_CALL_STATE_CHANGED, null);
phone.registerForDisconnect(mHandler, EVENT_DISCONNECT, null);
@@ -453,7 +480,7 @@
heldPhone = heldCall.getPhone();
}
- return (heldPhone == activePhone);
+ return heldPhone.getClass().equals(activePhone.getClass());
}
/**
@@ -466,10 +493,14 @@
* In these cases, this operation may not be performed.
*/
public void conference(Call heldCall) throws CallStateException {
- if (canConference(heldCall))
+ Phone fgPhone = getFgPhone();
+ if (fgPhone instanceof SipPhone) {
+ ((SipPhone) fgPhone).conference(heldCall);
+ } else if (canConference(heldCall)) {
+ fgPhone.conference();
+ } else {
throw(new CallStateException("Can't conference foreground and selected background call"));
-
- heldCall.getPhone().conference();
+ }
}
/**
@@ -1288,7 +1319,10 @@
mUnknownConnectionRegistrants.notifyRegistrants((AsyncResult) msg.obj);
break;
case EVENT_INCOMING_RING:
- mIncomingRingRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ // The event may come from RIL who's not aware of an ongoing fg call
+ if (!hasActiveFgCall()) {
+ mIncomingRingRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ }
break;
case EVENT_RINGBACK_TONE:
mRingbackToneRegistrants.notifyRegistrants((AsyncResult) msg.obj);
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
index a94518f..a052db0 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -21,6 +21,7 @@
import android.content.SharedPreferences;
import android.net.Uri;
import android.net.rtp.AudioGroup;
+import android.net.rtp.AudioStream;
import android.net.sip.SipAudioCall;
import android.net.sip.SipManager;
import android.net.sip.SipProfile;
@@ -221,7 +222,21 @@
}
public void conference() throws CallStateException {
- // TODO
+ if ((foregroundCall.getState() != SipCall.State.ACTIVE)
+ || (foregroundCall.getState() != SipCall.State.ACTIVE)) {
+ throw new CallStateException("wrong state to merge calls: fg="
+ + foregroundCall.getState() + ", bg="
+ + backgroundCall.getState());
+ }
+ foregroundCall.merge(backgroundCall);
+ }
+
+ public void conference(Call that) throws CallStateException {
+ if (!(that instanceof SipCall)) {
+ throw new CallStateException("expect " + SipCall.class
+ + ", cannot merge with " + that.getClass());
+ }
+ foregroundCall.merge((SipCall) that);
}
public boolean canTransfer() {
@@ -467,6 +482,18 @@
return (audioGroup.getMode() == AudioGroup.MODE_MUTED);
}
+ void merge(SipCall that) throws CallStateException {
+ AudioGroup myGroup = getAudioGroup();
+ for (Connection c : that.connections) {
+ SipConnection conn = (SipConnection) c;
+ conn.mergeTo(myGroup);
+ connections.add(conn);
+ conn.changeOwner(this);
+ }
+ that.connections.clear();
+ that.setState(Call.State.IDLE);
+ }
+
void sendDtmf(char c) {
AudioGroup audioGroup = getAudioGroup();
if (audioGroup == null) return;
@@ -541,6 +568,7 @@
private class SipConnection extends SipConnectionBase {
private SipCall mOwner;
private SipAudioCall mSipAudioCall;
+ private AudioGroup mOriginalGroup;
private Call.State mState = Call.State.IDLE;
private SipProfile mPeer;
private boolean mIncoming = false;
@@ -660,6 +688,16 @@
}
}
+ void mergeTo(AudioGroup group) throws CallStateException {
+ AudioStream stream = mSipAudioCall.getAudioStream();
+ if (stream == null) {
+ throw new CallStateException("wrong state to merge: "
+ + mSipAudioCall.getState());
+ }
+ if (mOriginalGroup == null) mOriginalGroup = getAudioGroup();
+ stream.join(group);
+ }
+
@Override
protected void setState(Call.State state) {
if (state == mState) return;
@@ -694,6 +732,8 @@
@Override
public void hangup() throws CallStateException {
+ // TODO: need to pull AudioStream out of the AudioGroup in case
+ // this conn was part of a conf call
Log.v(LOG_TAG, "hangup conn: " + mPeer.getUriString() + ": "
+ ": on phone " + getPhone());
try {
diff --git a/tests/BatteryWaster/res/layout/battery_waster.xml b/tests/BatteryWaster/res/layout/battery_waster.xml
index 36aa68b..57a5b55 100644
--- a/tests/BatteryWaster/res/layout/battery_waster.xml
+++ b/tests/BatteryWaster/res/layout/battery_waster.xml
@@ -25,6 +25,7 @@
android:layout_height="wrap_content"
android:layout_marginLeft="25dp"
android:layout_marginTop="25dp"
+ android:saveEnabled="false"
android:textSize="18sp"
android:textColor="#ffffffff"
android:text="@string/waste_away"
@@ -35,6 +36,7 @@
android:layout_height="wrap_content"
android:layout_marginLeft="25dp"
android:layout_marginTop="25dp"
+ android:saveEnabled="false"
android:textSize="18sp"
android:textColor="#ffffffff"
android:text="@string/wake_away"
diff --git a/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java b/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java
index 499330f..48c4520 100644
--- a/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java
+++ b/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java
@@ -39,6 +39,7 @@
DateFormat mDateFormat;
IntentFilter mFilter;
PowerManager.WakeLock mWakeLock;
+ PowerManager.WakeLock mPartialWakeLock;
SpinThread mThread;
boolean mWasting, mWaking;
@@ -66,6 +67,8 @@
PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "BatteryWaster");
mWakeLock.setReferenceCounted(false);
+ mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "BatteryWaster");
+ mPartialWakeLock.setReferenceCounted(false);
}
@Override
@@ -80,6 +83,9 @@
if (mWakeLock.isHeld()) {
mWakeLock.release();
}
+ if (mPartialWakeLock.isHeld()) {
+ mPartialWakeLock.release();
+ }
}
View.OnClickListener mClickListener = new View.OnClickListener() {
@@ -133,7 +139,7 @@
}
void updateWakeLock() {
- if (mWasting || mWaking) {
+ if (mWasting) {
if (!mWakeLock.isHeld()) {
mWakeLock.acquire();
}
@@ -142,6 +148,15 @@
mWakeLock.release();
}
}
+ if (mWaking) {
+ if (!mPartialWakeLock.isHeld()) {
+ mPartialWakeLock.acquire();
+ }
+ } else {
+ if (mPartialWakeLock.isHeld()) {
+ mPartialWakeLock.release();
+ }
+ }
}
void log(String s) {
diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java
index abdc9d7..f4be839 100644
--- a/voip/java/android/net/sip/SipAudioCall.java
+++ b/voip/java/android/net/sip/SipAudioCall.java
@@ -191,11 +191,8 @@
*/
void continueCall() throws SipException;
- /** Puts the device to in-call mode. */
- void setInCallMode();
-
/** Puts the device to speaker mode. */
- void setSpeakerMode();
+ void setSpeakerMode(boolean speakerMode);
/** Toggles mute. */
void toggleMute();
diff --git a/voip/java/android/net/sip/SipAudioCallImpl.java b/voip/java/android/net/sip/SipAudioCallImpl.java
index 474bc4b..7161309 100644
--- a/voip/java/android/net/sip/SipAudioCallImpl.java
+++ b/voip/java/android/net/sip/SipAudioCallImpl.java
@@ -485,16 +485,9 @@
return mMuted;
}
- public synchronized void setInCallMode() {
+ public synchronized void setSpeakerMode(boolean speakerMode) {
((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
- .setSpeakerphoneOn(false);
- ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
- .setMode(AudioManager.MODE_NORMAL);
- }
-
- public synchronized void setSpeakerMode() {
- ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE))
- .setSpeakerphoneOn(true);
+ .setSpeakerphoneOn(speakerMode);
}
public void sendDtmf(int code) {
@@ -587,8 +580,15 @@
Log.d(TAG, " not sending");
audioStream.setMode(RtpStream.MODE_RECEIVE_ONLY);
}
+ } else {
+ /* The recorder volume will be very low if the device is in
+ * IN_CALL mode. Therefore, we have to set the mode to NORMAL
+ * in order to have the normal microphone level.
+ */
+ ((AudioManager) mContext.getSystemService
+ (Context.AUDIO_SERVICE))
+ .setMode(AudioManager.MODE_NORMAL);
}
- setInCallMode();
AudioGroup audioGroup = new AudioGroup();
audioStream.join(audioGroup);
@@ -614,7 +614,6 @@
mRtpSession = null;
}
}
- setInCallMode();
}
private int getLocalMediaPort() {
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 4a22b68..9d21521 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -307,6 +307,16 @@
public static final String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
/**
+ * In this Wi-Fi lock mode, Wi-Fi will behave as in the mode
+ * {@link #WIFI_MODE_FULL} but it operates at high performance
+ * at the expense of power. This mode should be used
+ * only when the wifi connection needs to have minimum loss and low
+ * latency as it can impact the battery life.
+ * @hide
+ */
+ public static final int WIFI_MODE_FULL_HIGH_PERF = 3;
+
+ /**
* In this Wi-Fi lock mode, Wi-Fi will be kept active,
* and will behave normally, i.e., it will attempt to automatically
* establish a connection to a remembered access point that is
@@ -993,8 +1003,9 @@
/**
* Creates a new WifiLock.
*
- * @param lockType the type of lock to create. See {@link #WIFI_MODE_FULL} and
- * {@link #WIFI_MODE_SCAN_ONLY} for descriptions of the types of Wi-Fi locks.
+ * @param lockType the type of lock to create. See {@link #WIFI_MODE_FULL},
+ * and {@link #WIFI_MODE_SCAN_ONLY} for descriptions of the types of Wi-Fi locks.
+ *
* @param tag a tag for the WifiLock to identify it in debugging messages. This string is
* never shown to the user under normal conditions, but should be descriptive
* enough to identify your application and the specific WifiLock within it, if it
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 7a3282c..25f05c0 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -149,6 +149,8 @@
public native static String getDhcpError();
+ public native static boolean setSuspendOptimizationsCommand(boolean enabled);
+
/**
* Wait for the supplicant to send an event, returning the event string.
* @return the event string sent by the supplicant.
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index f57df62..0cc1f46 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -276,6 +276,9 @@
private boolean mIsScanModeActive;
private boolean mEnableRssiPolling;
+ private boolean mIsHighPerfEnabled;
+ private int mPowerModeRefCount = 0;
+ private int mOptimizationsDisabledRefCount = 0;
/**
* One of {@link WifiManager#WIFI_STATE_DISABLED},
@@ -659,6 +662,67 @@
}
}
+ /**
+ * Set suspend mode optimizations. These include:
+ * - packet filtering
+ * - turn off roaming
+ * - DTIM settings
+ *
+ * Uses reference counting to keep the suspend optimizations disabled
+ * as long as one entity wants optimizations disabled.
+ *
+ * For example, WifiLock can keep suspend optimizations disabled
+ * or the user setting (wifi never sleeps) can keep suspend optimizations
+ * disabled. As long as one entity wants it disabled, it should stay
+ * that way
+ *
+ * @param enabled true if optimizations need enabled, false otherwise
+ */
+ public synchronized void setSuspendModeOptimizations(boolean enabled) {
+
+ /* It is good to plumb suspend optimization enable
+ * or disable even if ref count indicates already done
+ * since we could have a case of previous failure.
+ */
+ if (!enabled) {
+ mOptimizationsDisabledRefCount++;
+ } else {
+ mOptimizationsDisabledRefCount--;
+ if (mOptimizationsDisabledRefCount > 0) {
+ return;
+ } else {
+ /* Keep refcount from becoming negative */
+ mOptimizationsDisabledRefCount = 0;
+ }
+ }
+
+ if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
+ return;
+ }
+
+ WifiNative.setSuspendOptimizationsCommand(enabled);
+ }
+
+
+ /**
+ * Set high performance mode of operation. This would mean
+ * use active power mode and disable suspend optimizations
+ * @param enabled true if enabled, false otherwise
+ */
+ public synchronized void setHighPerfMode(boolean enabled) {
+ if (mIsHighPerfEnabled != enabled) {
+ if (enabled) {
+ setPowerMode(DRIVER_POWER_MODE_ACTIVE);
+ setSuspendModeOptimizations(false);
+ } else {
+ setPowerMode(DRIVER_POWER_MODE_AUTO);
+ setSuspendModeOptimizations(true);
+ }
+ mIsHighPerfEnabled = enabled;
+ Log.d(TAG,"high performance mode: " + enabled);
+ }
+ }
+
private void checkIsBluetoothPlaying() {
boolean isBluetoothPlaying = false;
@@ -744,6 +808,9 @@
dhcpThread.start();
mDhcpTarget = new DhcpHandler(dhcpThread.getLooper(), this);
mIsScanModeActive = true;
+ mIsHighPerfEnabled = false;
+ mOptimizationsDisabledRefCount = 0;
+ mPowerModeRefCount = 0;
mTornDownByConnMgr = false;
mLastBssid = null;
mLastSsid = null;
@@ -1947,13 +2014,41 @@
* @param mode
* DRIVER_POWER_MODE_AUTO
* DRIVER_POWER_MODE_ACTIVE
- * @return {@code true} if the operation succeeds, {@code false} otherwise
+ *
+ * Uses reference counting to keep power mode active
+ * as long as one entity wants power mode to be active.
+ *
+ * For example, WifiLock high perf mode can keep power mode active
+ * or a DHCP session can keep it active. As long as one entity wants
+ * it enabled, it should stay that way
+ *
*/
- public synchronized boolean setPowerMode(int mode) {
- if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
- return false;
+ private synchronized void setPowerMode(int mode) {
+
+ /* It is good to plumb power mode change
+ * even if ref count indicates already done
+ * since we could have a case of previous failure.
+ */
+ switch(mode) {
+ case DRIVER_POWER_MODE_ACTIVE:
+ mPowerModeRefCount++;
+ break;
+ case DRIVER_POWER_MODE_AUTO:
+ mPowerModeRefCount--;
+ if (mPowerModeRefCount > 0) {
+ return;
+ } else {
+ /* Keep refcount from becoming negative */
+ mPowerModeRefCount = 0;
+ }
+ break;
}
- return WifiNative.setPowerModeCommand(mode);
+
+ if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
+ return;
+ }
+
+ WifiNative.setPowerModeCommand(mode);
}
/**