Merge "docs: fix markup in javadocs Change-Id: I1f9be842f40c813134fbcd5fdbbead5647fb72e5"
diff --git a/CleanSpec.mk b/CleanSpec.mk
index d48904c..6ae887b 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -111,6 +111,8 @@
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/wifi/java)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/wifi/java)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/fonts/Lohit_Hindi.ttf)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp
index 6fa66cf..263ecd1 100644
--- a/cmds/stagefright/sf2.cpp
+++ b/cmds/stagefright/sf2.cpp
@@ -569,12 +569,16 @@
CHECK(control->isValid());
SurfaceComposerClient::openGlobalTransaction();
- CHECK_EQ(control->setLayer(30000), (status_t)OK);
+ CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK);
CHECK_EQ(control->show(), (status_t)OK);
SurfaceComposerClient::closeGlobalTransaction();
surface = control->getSurface();
CHECK(surface != NULL);
+
+ CHECK_EQ((status_t)OK,
+ native_window_api_connect(
+ surface.get(), NATIVE_WINDOW_API_MEDIA));
}
sp<Controller> controller =
@@ -589,6 +593,10 @@
looper->unregisterHandler(controller->id());
if (!decodeAudio && useSurface) {
+ CHECK_EQ((status_t)OK,
+ native_window_api_disconnect(
+ surface.get(), NATIVE_WINDOW_API_MEDIA));
+
composerClient->dispose();
}
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index a126e83..528d197 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -917,7 +917,7 @@
CHECK(control->isValid());
SurfaceComposerClient::openGlobalTransaction();
- CHECK_EQ(control->setLayer(30000), (status_t)OK);
+ CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK);
CHECK_EQ(control->show(), (status_t)OK);
SurfaceComposerClient::closeGlobalTransaction();
@@ -929,6 +929,10 @@
sp<SurfaceTexture> texture = new SurfaceTexture(0 /* tex */);
gSurface = new SurfaceTextureClient(texture);
}
+
+ CHECK_EQ((status_t)OK,
+ native_window_api_connect(
+ gSurface.get(), NATIVE_WINDOW_API_MEDIA));
}
DataSource::RegisterDefaultSniffers();
@@ -1122,6 +1126,10 @@
}
if ((useSurfaceAlloc || useSurfaceTexAlloc) && !audioOnly) {
+ CHECK_EQ((status_t)OK,
+ native_window_api_disconnect(
+ gSurface.get(), NATIVE_WINDOW_API_MEDIA));
+
gSurface.clear();
if (useSurfaceAlloc) {
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index b13236a..2378345 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -323,7 +323,7 @@
CHECK(control->isValid());
SurfaceComposerClient::openGlobalTransaction();
- CHECK_EQ(control->setLayer(30000), (status_t)OK);
+ CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK);
CHECK_EQ(control->show(), (status_t)OK);
SurfaceComposerClient::closeGlobalTransaction();
diff --git a/core/java/android/content/pm/PackageInfoLite.java b/core/java/android/content/pm/PackageInfoLite.java
index da97fde0..9625944 100644
--- a/core/java/android/content/pm/PackageInfoLite.java
+++ b/core/java/android/content/pm/PackageInfoLite.java
@@ -41,6 +41,8 @@
public int recommendedInstallLocation;
public int installLocation;
+ public VerifierInfo[] verifiers;
+
public PackageInfoLite() {
}
@@ -58,6 +60,13 @@
dest.writeString(packageName);
dest.writeInt(recommendedInstallLocation);
dest.writeInt(installLocation);
+
+ if (verifiers == null || verifiers.length == 0) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(verifiers.length);
+ dest.writeTypedArray(verifiers, parcelableFlags);
+ }
}
public static final Parcelable.Creator<PackageInfoLite> CREATOR
@@ -75,5 +84,13 @@
packageName = source.readString();
recommendedInstallLocation = source.readInt();
installLocation = source.readInt();
+
+ final int verifiersLength = source.readInt();
+ if (verifiersLength == 0) {
+ verifiers = new VerifierInfo[0];
+ } else {
+ verifiers = new VerifierInfo[verifiersLength];
+ source.readTypedArray(verifiers, VerifierInfo.CREATOR);
+ }
}
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index ef7e233..d45a71a 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -725,6 +725,16 @@
public static final int MOVE_EXTERNAL_MEDIA = 0x00000002;
/**
+ * Usable by the required verifier as the {@code verificationCode} argument
+ * for {@link PackageManager#verifyPendingInstall} to indicate that it will
+ * allow the installation to proceed without any of the optional verifiers
+ * needing to vote.
+ *
+ * @hide
+ */
+ public static final int VERIFICATION_ALLOW_WITHOUT_SUFFICIENT = 2;
+
+ /**
* Used as the {@code verificationCode} argument for
* {@link PackageManager#verifyPendingInstall} to indicate that the calling
* package verifier allows the installation to proceed.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index e7b844c..c30675b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -28,7 +28,9 @@
import android.os.Bundle;
import android.os.PatternMatcher;
import android.util.AttributeSet;
+import android.util.Base64;
import android.util.DisplayMetrics;
+import android.util.Log;
import android.util.Slog;
import android.util.TypedValue;
import com.android.internal.util.XmlUtils;
@@ -40,11 +42,18 @@
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
+import java.security.spec.EncodedKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
+import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
@@ -150,12 +159,14 @@
* @hide
*/
public static class PackageLite {
- public String packageName;
- public int installLocation;
- public String mScanPath;
- public PackageLite(String packageName, int installLocation) {
+ public final String packageName;
+ public final int installLocation;
+ public final VerifierInfo[] verifiers;
+
+ public PackageLite(String packageName, int installLocation, List<VerifierInfo> verifiers) {
this.packageName = packageName;
this.installLocation = installLocation;
+ this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]);
}
}
@@ -619,8 +630,9 @@
* @return PackageLite object with package information or null on failure.
*/
public static PackageLite parsePackageLite(String packageFilePath, int flags) {
- XmlResourceParser parser = null;
AssetManager assmgr = null;
+ final XmlResourceParser parser;
+ final Resources res;
try {
assmgr = new AssetManager();
assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -631,6 +643,9 @@
return null;
}
+ final DisplayMetrics metrics = new DisplayMetrics();
+ metrics.setToDefaults();
+ res = new Resources(assmgr, metrics, null);
parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
} catch (Exception e) {
if (assmgr != null) assmgr.close();
@@ -638,11 +653,12 @@
+ packageFilePath, e);
return null;
}
- AttributeSet attrs = parser;
- String errors[] = new String[1];
+
+ final AttributeSet attrs = parser;
+ final String errors[] = new String[1];
PackageLite packageLite = null;
try {
- packageLite = parsePackageLite(parser, attrs, flags, errors);
+ packageLite = parsePackageLite(res, parser, attrs, flags, errors);
} catch (IOException e) {
Slog.w(TAG, packageFilePath, e);
} catch (XmlPullParserException e) {
@@ -719,9 +735,9 @@
return pkgName.intern();
}
- private static PackageLite parsePackageLite(XmlPullParser parser,
- AttributeSet attrs, int flags, String[] outError)
- throws IOException, XmlPullParserException {
+ private static PackageLite parsePackageLite(Resources res, XmlPullParser parser,
+ AttributeSet attrs, int flags, String[] outError) throws IOException,
+ XmlPullParserException {
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
@@ -759,7 +775,26 @@
break;
}
}
- return new PackageLite(pkgName.intern(), installLocation);
+
+ // Only search the tree when the tag is directly below <manifest>
+ final int searchDepth = parser.getDepth() + 1;
+
+ final List<VerifierInfo> verifiers = new ArrayList<VerifierInfo>();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() >= searchDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ if (parser.getDepth() == searchDepth && "package-verifier".equals(parser.getName())) {
+ final VerifierInfo verifier = parseVerifier(res, parser, attrs, flags, outError);
+ if (verifier != null) {
+ verifiers.add(verifier);
+ }
+ }
+ }
+
+ return new PackageLite(pkgName.intern(), installLocation, verifiers);
}
/**
@@ -2691,6 +2726,63 @@
return data;
}
+ private static VerifierInfo parseVerifier(Resources res, XmlPullParser parser,
+ AttributeSet attrs, int flags, String[] outError) throws XmlPullParserException,
+ IOException {
+ final TypedArray sa = res.obtainAttributes(attrs,
+ com.android.internal.R.styleable.AndroidManifestPackageVerifier);
+
+ final String packageName = sa.getNonResourceString(
+ com.android.internal.R.styleable.AndroidManifestPackageVerifier_name);
+
+ final String encodedPublicKey = sa.getNonResourceString(
+ com.android.internal.R.styleable.AndroidManifestPackageVerifier_publicKey);
+
+ sa.recycle();
+
+ if (packageName == null || packageName.length() == 0) {
+ Slog.i(TAG, "verifier package name was null; skipping");
+ return null;
+ } else if (encodedPublicKey == null) {
+ Slog.i(TAG, "verifier " + packageName + " public key was null; skipping");
+ }
+
+ EncodedKeySpec keySpec;
+ try {
+ final byte[] encoded = Base64.decode(encodedPublicKey, Base64.DEFAULT);
+ keySpec = new X509EncodedKeySpec(encoded);
+ } catch (IllegalArgumentException e) {
+ Slog.i(TAG, "Could not parse verifier " + packageName + " public key; invalid Base64");
+ return null;
+ }
+
+ /* First try the key as an RSA key. */
+ try {
+ final KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+ final PublicKey publicKey = keyFactory.generatePublic(keySpec);
+ return new VerifierInfo(packageName, publicKey);
+ } catch (NoSuchAlgorithmException e) {
+ Log.wtf(TAG, "Could not parse public key because RSA isn't included in build");
+ return null;
+ } catch (InvalidKeySpecException e) {
+ // Not a RSA public key.
+ }
+
+ /* Now try it as a DSA key. */
+ try {
+ final KeyFactory keyFactory = KeyFactory.getInstance("DSA");
+ final PublicKey publicKey = keyFactory.generatePublic(keySpec);
+ return new VerifierInfo(packageName, publicKey);
+ } catch (NoSuchAlgorithmException e) {
+ Log.wtf(TAG, "Could not parse public key because DSA isn't included in build");
+ return null;
+ } catch (InvalidKeySpecException e) {
+ // Not a DSA public key.
+ }
+
+ return null;
+ }
+
private static final String ANDROID_RESOURCES
= "http://schemas.android.com/apk/res/android";
diff --git a/core/java/android/content/pm/Signature.java b/core/java/android/content/pm/Signature.java
index c6aefb8..9c9340d 100644
--- a/core/java/android/content/pm/Signature.java
+++ b/core/java/android/content/pm/Signature.java
@@ -19,7 +19,12 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.io.ByteArrayInputStream;
import java.lang.ref.SoftReference;
+import java.security.PublicKey;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
import java.util.Arrays;
/**
@@ -135,6 +140,20 @@
return bytes;
}
+ /**
+ * Returns the public key for this signature.
+ *
+ * @throws CertificateException when Signature isn't a valid X.509
+ * certificate; shouldn't happen.
+ * @hide
+ */
+ public PublicKey getPublicKey() throws CertificateException {
+ final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ final ByteArrayInputStream bais = new ByteArrayInputStream(mSignature);
+ final Certificate cert = certFactory.generateCertificate(bais);
+ return cert.getPublicKey();
+ }
+
@Override
public boolean equals(Object obj) {
try {
diff --git a/core/java/android/content/pm/VerifierInfo.aidl b/core/java/android/content/pm/VerifierInfo.aidl
new file mode 100644
index 0000000..7702d38
--- /dev/null
+++ b/core/java/android/content/pm/VerifierInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2011, 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.content.pm;
+
+parcelable VerifierInfo;
diff --git a/core/java/android/content/pm/VerifierInfo.java b/core/java/android/content/pm/VerifierInfo.java
new file mode 100644
index 0000000..0a2b283
--- /dev/null
+++ b/core/java/android/content/pm/VerifierInfo.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2011 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.content.pm;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.security.PublicKey;
+
+/**
+ * Contains information about a package verifier as used by
+ * {@code PackageManagerService} during package verification.
+ *
+ * @hide
+ */
+public class VerifierInfo implements Parcelable {
+ /** Package name of the verifier. */
+ public final String packageName;
+
+ /** Signatures used to sign the package verifier's package. */
+ public final PublicKey publicKey;
+
+ /**
+ * Creates an object that represents a verifier info object.
+ *
+ * @param packageName the package name in Java-style. Must not be {@code
+ * null} or empty.
+ * @param publicKey the public key for the signer encoded in Base64. Must
+ * not be {@code null} or empty.
+ * @throws IllegalArgumentException if either argument is null or empty.
+ */
+ public VerifierInfo(String packageName, PublicKey publicKey) {
+ if (packageName == null || packageName.length() == 0) {
+ throw new IllegalArgumentException("packageName must not be null or empty");
+ } else if (publicKey == null) {
+ throw new IllegalArgumentException("publicKey must not be null");
+ }
+
+ this.packageName = packageName;
+ this.publicKey = publicKey;
+ }
+
+ private VerifierInfo(Parcel source) {
+ packageName = source.readString();
+ publicKey = (PublicKey) source.readSerializable();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(packageName);
+ dest.writeSerializable(publicKey);
+ }
+
+ public static final Parcelable.Creator<VerifierInfo> CREATOR
+ = new Parcelable.Creator<VerifierInfo>() {
+ public VerifierInfo createFromParcel(Parcel source) {
+ return new VerifierInfo(source);
+ }
+
+ public VerifierInfo[] newArray(int size) {
+ return new VerifierInfo[size];
+ }
+ };
+}
\ No newline at end of file
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index a5cdf70..a6635be 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -270,6 +270,11 @@
|| entry.operations < 0) {
throw new IllegalArgumentException("tried recording negative data");
}
+ if (entry.rxBytes == 0 && entry.rxPackets == 0 && entry.txBytes == 0 && entry.txPackets == 0
+ && entry.operations == 0) {
+ // nothing to record; skip
+ return;
+ }
// create any buckets needed by this range
ensureBuckets(start, end);
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index cd49023..418b82f 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -19,14 +19,15 @@
import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.ConnectivityManager.TYPE_WIMAX;
-import static android.net.ConnectivityManager.isNetworkTypeMobile;
import static android.net.NetworkIdentity.scrubSubscriberId;
import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G;
import static android.telephony.TelephonyManager.NETWORK_CLASS_3_G;
import static android.telephony.TelephonyManager.NETWORK_CLASS_4_G;
import static android.telephony.TelephonyManager.NETWORK_CLASS_UNKNOWN;
import static android.telephony.TelephonyManager.getNetworkClass;
+import static com.android.internal.util.ArrayUtils.contains;
+import android.content.res.Resources;
import android.os.Parcel;
import android.os.Parcelable;
@@ -52,6 +53,16 @@
public static final int MATCH_ETHERNET = 5;
/**
+ * Set of {@link NetworkInfo#getType()} that reflect data usage.
+ */
+ private static final int[] DATA_USAGE_NETWORK_TYPES;
+
+ static {
+ DATA_USAGE_NETWORK_TYPES = Resources.getSystem().getIntArray(
+ com.android.internal.R.array.config_data_usage_network_types);
+ }
+
+ /**
* Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style
* networks together. Only uses statistics for requested IMSI.
*/
@@ -151,7 +162,7 @@
}
/**
- * Test if this network matches the given template and IMSI.
+ * Test if given {@link NetworkIdentity} matches this template.
*/
public boolean matches(NetworkIdentity ident) {
switch (mMatchRule) {
@@ -171,23 +182,25 @@
}
/**
- * Check if mobile network with matching IMSI. Also matches
- * {@link #TYPE_WIMAX}.
+ * Check if mobile network with matching IMSI.
*/
private boolean matchesMobile(NetworkIdentity ident) {
- if (isNetworkTypeMobile(ident.mType) && Objects.equal(mSubscriberId, ident.mSubscriberId)) {
+ if (ident.mType == TYPE_WIMAX) {
+ // TODO: consider matching against WiMAX subscriber identity
return true;
- } else if (ident.mType == TYPE_WIMAX) {
- return true;
+ } else {
+ return (contains(DATA_USAGE_NETWORK_TYPES, ident.mType)
+ && Objects.equal(mSubscriberId, ident.mSubscriberId));
}
- return false;
}
/**
* Check if mobile network classified 3G or lower with matching IMSI.
*/
private boolean matchesMobile3gLower(NetworkIdentity ident) {
- if (isNetworkTypeMobile(ident.mType) && Objects.equal(mSubscriberId, ident.mSubscriberId)) {
+ if (ident.mType == TYPE_WIMAX) {
+ return false;
+ } else if (matchesMobile(ident)) {
switch (getNetworkClass(ident.mSubType)) {
case NETWORK_CLASS_UNKNOWN:
case NETWORK_CLASS_2_G:
@@ -199,17 +212,17 @@
}
/**
- * Check if mobile network classified 4G with matching IMSI. Also matches
- * {@link #TYPE_WIMAX}.
+ * Check if mobile network classified 4G with matching IMSI.
*/
private boolean matchesMobile4g(NetworkIdentity ident) {
- if (isNetworkTypeMobile(ident.mType) && Objects.equal(mSubscriberId, ident.mSubscriberId)) {
+ if (ident.mType == TYPE_WIMAX) {
+ // TODO: consider matching against WiMAX subscriber identity
+ return true;
+ } else if (matchesMobile(ident)) {
switch (getNetworkClass(ident.mSubType)) {
case NETWORK_CLASS_4_G:
return true;
}
- } else if (ident.mType == TYPE_WIMAX) {
- return true;
}
return false;
}
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 2f9852d..f82c9c4 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -275,7 +275,7 @@
}
if (reflowed == null) {
- reflowed = new StaticLayout(getText());
+ reflowed = new StaticLayout(null);
} else {
reflowed.prepare();
}
@@ -488,7 +488,7 @@
private int mTopPadding, mBottomPadding;
- private static StaticLayout sStaticLayout = null;
+ private static StaticLayout sStaticLayout = new StaticLayout(null);
private static final Object[] sLock = new Object[0];
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index 5fed775..fdbec20 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -709,8 +709,6 @@
T ret1 = null;
for (int i = 0; i < spanCount; i++) {
- if (!kind.isInstance(spans[i])) continue;
-
int spanStart = starts[i];
int spanEnd = ends[i];
@@ -735,6 +733,9 @@
continue;
}
+ // Expensive test, should be performed after the previous tests
+ if (!kind.isInstance(spans[i])) continue;
+
if (count == 0) {
// Safe conversion thanks to the isInstance test above
ret1 = (T) spans[i];
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index e8b2045..583cbe6 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -763,7 +763,8 @@
return;
}
- float ellipsisWidth = paint.measureText(HORIZONTAL_ELLIPSIS);
+ float ellipsisWidth = paint.measureText(
+ (where == TextUtils.TruncateAt.END_SMALL) ? ELLIPSIS_TWO_DOTS : ELLIPSIS_NORMAL);
int ellipsisStart = 0;
int ellipsisCount = 0;
int len = lineEnd - lineStart;
@@ -791,7 +792,8 @@
Log.w(TAG, "Start Ellipsis only supported with one line");
}
}
- } else if (where == TextUtils.TruncateAt.END || where == TextUtils.TruncateAt.MARQUEE) {
+ } else if (where == TextUtils.TruncateAt.END || where == TextUtils.TruncateAt.MARQUEE ||
+ where == TextUtils.TruncateAt.END_SMALL) {
float sum = 0;
int i;
@@ -1001,7 +1003,9 @@
private static final char CHAR_HYPHEN = '-';
private static final double EXTRA_ROUNDING = 0.5;
- private static final String HORIZONTAL_ELLIPSIS = "\u2026"; // this is "..."
+
+ private static final String ELLIPSIS_NORMAL = "\u2026"; // this is "..."
+ private static final String ELLIPSIS_TWO_DOTS = "\u2025"; // this is ".."
private static final int CHAR_FIRST_HIGH_SURROGATE = 0xD800;
private static final int CHAR_LAST_LOW_SURROGATE = 0xDFFF;
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 1e17632..68fea19 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -30,6 +30,8 @@
import com.android.internal.util.ArrayUtils;
+import java.lang.reflect.Array;
+
/**
* Represents a line of styled text, for measuring in visual order and
* for rendering.
@@ -850,6 +852,73 @@
return runIsRtl ? -ret : ret;
}
+ private static class SpanSet<E> {
+ final int numberOfSpans;
+ final E[] spans;
+ final int[] spanStarts;
+ final int[] spanEnds;
+ final int[] spanFlags;
+
+ @SuppressWarnings("unchecked")
+ SpanSet(Spanned spanned, int start, int limit, Class<? extends E> type) {
+ final E[] allSpans = spanned.getSpans(start, limit, type);
+ final int length = allSpans.length;
+ // These arrays may end up being too large because of empty spans
+ spans = (E[]) Array.newInstance(type, length);
+ spanStarts = new int[length];
+ spanEnds = new int[length];
+ spanFlags = new int[length];
+
+ int count = 0;
+ for (int i = 0; i < length; i++) {
+ final E span = allSpans[i];
+
+ final int spanStart = spanned.getSpanStart(span);
+ final int spanEnd = spanned.getSpanEnd(span);
+ if (spanStart == spanEnd) continue;
+
+ final int spanFlag = spanned.getSpanFlags(span);
+ final int priority = spanFlag & Spanned.SPAN_PRIORITY;
+ if (priority != 0 && count != 0) {
+ int j;
+
+ for (j = 0; j < count; j++) {
+ final int otherPriority = spanFlags[j] & Spanned.SPAN_PRIORITY;
+ if (priority > otherPriority) break;
+ }
+
+ System.arraycopy(spans, j, spans, j + 1, count - j);
+ System.arraycopy(spanStarts, j, spanStarts, j + 1, count - j);
+ System.arraycopy(spanEnds, j, spanEnds, j + 1, count - j);
+ System.arraycopy(spanFlags, j, spanFlags, j + 1, count - j);
+
+ spans[j] = span;
+ spanStarts[j] = spanStart;
+ spanEnds[j] = spanEnd;
+ spanFlags[j] = spanFlag;
+ } else {
+ spans[i] = span;
+ spanStarts[i] = spanStart;
+ spanEnds[i] = spanEnd;
+ spanFlags[i] = spanFlag;
+ }
+
+ count++;
+ }
+ numberOfSpans = count;
+ }
+
+ int getNextTransition(int start, int limit) {
+ for (int i = 0; i < numberOfSpans; i++) {
+ final int spanStart = spanStarts[i];
+ final int spanEnd = spanEnds[i];
+ if (spanStart > start && spanStart < limit) limit = spanStart;
+ if (spanEnd > start && spanEnd < limit) limit = spanEnd;
+ }
+ return limit;
+ }
+ }
+
/**
* Utility function for handling a unidirectional run. The run must not
* contain tabs or emoji but can contain styles.
@@ -883,66 +952,70 @@
return 0f;
}
+ if (mSpanned == null) {
+ TextPaint wp = mWorkPaint;
+ wp.set(mPaint);
+ final int mlimit = measureLimit;
+ return handleText(wp, start, mlimit, start, limit, runIsRtl, c, x, top,
+ y, bottom, fmi, needWidth || mlimit < measureLimit);
+ }
+
+ final SpanSet<MetricAffectingSpan> metricAffectingSpans = new SpanSet<MetricAffectingSpan>(
+ mSpanned, mStart + start, mStart + limit, MetricAffectingSpan.class);
+ final SpanSet<CharacterStyle> characterStyleSpans = new SpanSet<CharacterStyle>(
+ mSpanned, mStart + start, mStart + limit, CharacterStyle.class);
+
// Shaping needs to take into account context up to metric boundaries,
// but rendering needs to take into account character style boundaries.
// So we iterate through metric runs to get metric bounds,
// then within each metric run iterate through character style runs
// for the run bounds.
- float ox = x;
+ final float originalX = x;
for (int i = start, inext; i < measureLimit; i = inext) {
TextPaint wp = mWorkPaint;
wp.set(mPaint);
- int mlimit;
- if (mSpanned == null) {
- inext = limit;
- mlimit = measureLimit;
- } else {
- inext = mSpanned.nextSpanTransition(mStart + i, mStart + limit,
- MetricAffectingSpan.class) - mStart;
+ inext = metricAffectingSpans.getNextTransition(mStart + i, mStart + limit) - mStart;
+ int mlimit = Math.min(inext, measureLimit);
- mlimit = inext < measureLimit ? inext : measureLimit;
- MetricAffectingSpan[] spans = mSpanned.getSpans(mStart + i,
- mStart + mlimit, MetricAffectingSpan.class);
- spans = TextUtils.removeEmptySpans(spans, mSpanned, MetricAffectingSpan.class);
+ ReplacementSpan replacement = null;
- if (spans.length > 0) {
- ReplacementSpan replacement = null;
- for (int j = 0; j < spans.length; j++) {
- MetricAffectingSpan span = spans[j];
- if (span instanceof ReplacementSpan) {
- replacement = (ReplacementSpan)span;
- } else {
- // We might have a replacement that uses the draw
- // state, otherwise measure state would suffice.
- span.updateDrawState(wp);
- }
- }
-
- if (replacement != null) {
- x += handleReplacement(replacement, wp, i,
- mlimit, runIsRtl, c, x, top, y, bottom, fmi,
- needWidth || mlimit < measureLimit);
- continue;
- }
+ for (int j = 0; j < metricAffectingSpans.numberOfSpans; j++) {
+ // Both intervals [spanStarts..spanEnds] and [mStart + i..mStart + mlimit] are NOT
+ // empty by construction. This special case in getSpans() explains the >= & <= tests
+ if ((metricAffectingSpans.spanStarts[j] >= mStart + mlimit) ||
+ (metricAffectingSpans.spanEnds[j] <= mStart + i)) continue;
+ MetricAffectingSpan span = metricAffectingSpans.spans[j];
+ if (span instanceof ReplacementSpan) {
+ replacement = (ReplacementSpan)span;
+ } else {
+ // We might have a replacement that uses the draw
+ // state, otherwise measure state would suffice.
+ span.updateDrawState(wp);
}
}
- if (mSpanned == null || c == null) {
+ if (replacement != null) {
+ x += handleReplacement(replacement, wp, i, mlimit, runIsRtl, c, x, top, y,
+ bottom, fmi, needWidth || mlimit < measureLimit);
+ continue;
+ }
+
+ if (c == null) {
x += handleText(wp, i, mlimit, i, inext, runIsRtl, c, x, top,
y, bottom, fmi, needWidth || mlimit < measureLimit);
} else {
for (int j = i, jnext; j < mlimit; j = jnext) {
- jnext = mSpanned.nextSpanTransition(mStart + j,
- mStart + mlimit, CharacterStyle.class) - mStart;
-
- CharacterStyle[] spans = mSpanned.getSpans(mStart + j,
- mStart + jnext, CharacterStyle.class);
- spans = TextUtils.removeEmptySpans(spans, mSpanned, CharacterStyle.class);
+ jnext = characterStyleSpans.getNextTransition(mStart + j, mStart + mlimit) -
+ mStart;
wp.set(mPaint);
- for (int k = 0; k < spans.length; k++) {
- CharacterStyle span = spans[k];
+ for (int k = 0; k < characterStyleSpans.numberOfSpans; k++) {
+ // Intentionally using >= and <= as explained above
+ if ((characterStyleSpans.spanStarts[k] >= mStart + jnext) ||
+ (characterStyleSpans.spanEnds[k] <= mStart + j)) continue;
+
+ CharacterStyle span = characterStyleSpans.spans[k];
span.updateDrawState(wp);
}
@@ -952,7 +1025,7 @@
}
}
- return x - ox;
+ return x - originalX;
}
/**
@@ -997,8 +1070,7 @@
}
pos += mStart;
- MetricAffectingSpan[] spans = mSpanned.getSpans(pos, pos + 1,
- MetricAffectingSpan.class);
+ MetricAffectingSpan[] spans = mSpanned.getSpans(pos, pos + 1, MetricAffectingSpan.class);
if (spans.length == 0) {
return mPaint.ascent();
}
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 894afbd..95a3cdc 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -53,9 +53,8 @@
import java.util.regex.Pattern;
public class TextUtils {
- private TextUtils() { /* cannot be instantiated */ }
- private static String[] EMPTY_STRING_ARRAY = new String[]{};
+ private TextUtils() { /* cannot be instantiated */ }
public static void getChars(CharSequence s, int start, int end,
char[] dest, int destoff) {
@@ -1000,6 +999,10 @@
MIDDLE,
END,
MARQUEE,
+ /**
+ * @hide
+ */
+ END_SMALL
}
public interface EllipsizeCallback {
@@ -1010,8 +1013,6 @@
public void ellipsized(int start, int end);
}
- private static String sEllipsis = null;
-
/**
* Returns the original text if it fits in the specified width
* given the properties of the specified Paint,
@@ -1042,7 +1043,8 @@
boolean preserveLength,
EllipsizeCallback callback) {
return ellipsize(text, paint, avail, where, preserveLength, callback,
- TextDirectionHeuristics.FIRSTSTRONG_LTR);
+ TextDirectionHeuristics.FIRSTSTRONG_LTR,
+ (where == TruncateAt.END_SMALL) ? ELLIPSIS_TWO_DOTS : ELLIPSIS_NORMAL);
}
/**
@@ -1063,11 +1065,7 @@
float avail, TruncateAt where,
boolean preserveLength,
EllipsizeCallback callback,
- TextDirectionHeuristic textDir) {
- if (sEllipsis == null) {
- Resources r = Resources.getSystem();
- sEllipsis = r.getString(R.string.ellipsis);
- }
+ TextDirectionHeuristic textDir, String ellipsis) {
int len = text.length();
@@ -1085,7 +1083,7 @@
// XXX assumes ellipsis string does not require shaping and
// is unaffected by style
- float ellipsiswid = paint.measureText(sEllipsis);
+ float ellipsiswid = paint.measureText(ellipsis);
avail -= ellipsiswid;
int left = 0;
@@ -1094,7 +1092,7 @@
// it all goes
} else if (where == TruncateAt.START) {
right = len - mt.breakText(0, len, false, avail);
- } else if (where == TruncateAt.END) {
+ } else if (where == TruncateAt.END || where == TruncateAt.END_SMALL) {
left = mt.breakText(0, len, true, avail);
} else {
right = len - mt.breakText(0, len, false, avail / 2);
@@ -1112,10 +1110,10 @@
int remaining = len - (right - left);
if (preserveLength) {
if (remaining > 0) { // else eliminate the ellipsis too
- buf[left++] = '\u2026';
+ buf[left++] = ellipsis.charAt(0);
}
for (int i = left; i < right; i++) {
- buf[i] = '\uFEFF';
+ buf[i] = ZWNBS_CHAR;
}
String s = new String(buf, 0, len);
if (sp == null) {
@@ -1131,16 +1129,16 @@
}
if (sp == null) {
- StringBuilder sb = new StringBuilder(remaining + sEllipsis.length());
+ StringBuilder sb = new StringBuilder(remaining + ellipsis.length());
sb.append(buf, 0, left);
- sb.append(sEllipsis);
+ sb.append(ellipsis);
sb.append(buf, right, len - right);
return sb.toString();
}
SpannableStringBuilder ssb = new SpannableStringBuilder();
ssb.append(text, 0, left);
- ssb.append(sEllipsis);
+ ssb.append(ellipsis);
ssb.append(text, right, len);
return ssb;
} finally {
@@ -1664,4 +1662,13 @@
private static Object sLock = new Object();
private static char[] sTemp = null;
+
+ private static String[] EMPTY_STRING_ARRAY = new String[]{};
+
+ private static final char ZWNBS_CHAR = '\uFEFF';
+
+ private static final String ELLIPSIS_NORMAL = Resources.getSystem().getString(
+ R.string.ellipsis);
+ private static final String ELLIPSIS_TWO_DOTS = Resources.getSystem().getString(
+ R.string.ellipsis_two_dots);
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index a29cf13..28e4485 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2824,6 +2824,13 @@
if (hasDisplayList) {
displayList = child.getDisplayList();
+ if (!displayList.isValid()) {
+ // Uncommon, but possible. If a view is removed from the hierarchy during the call
+ // to getDisplayList(), the display list will be marked invalid and we should not
+ // try to use it again.
+ displayList = null;
+ hasDisplayList = false;
+ }
}
if (hasNoCache) {
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 9c7b2a9..4f67675 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -881,6 +881,13 @@
public boolean rotationHasCompatibleMetricsLw(int orientation, int rotation);
/**
+ * Called by the window manager when the rotation changes.
+ *
+ * @param rotation The new rotation.
+ */
+ public void setRotationLw(int rotation);
+
+ /**
* Called when the system is mostly done booting to determine whether
* the system should go into safe mode.
*/
diff --git a/core/java/android/view/WindowOrientationListener.java b/core/java/android/view/WindowOrientationListener.java
index 726bf4a..c3c74a7 100755
--- a/core/java/android/view/WindowOrientationListener.java
+++ b/core/java/android/view/WindowOrientationListener.java
@@ -26,7 +26,7 @@
/**
* A special helper class used by the WindowManager
- * for receiving notifications from the SensorManager when
+ * for receiving notifications from the SensorManager when
* the orientation of the device has changed.
*
* NOTE: If changing anything here, please run the API demo
@@ -54,6 +54,7 @@
private Sensor mSensor;
private SensorEventListenerImpl mSensorEventListener;
boolean mLogEnabled;
+ int mCurrentRotation = -1;
/**
* Creates a new WindowOrientationListener.
@@ -117,12 +118,25 @@
}
/**
- * Gets the current orientation.
- * @return The current rotation, or -1 if unknown.
+ * Sets the current rotation.
+ *
+ * @param rotation The current rotation.
*/
- public int getCurrentRotation() {
+ public void setCurrentRotation(int rotation) {
+ mCurrentRotation = rotation;
+ }
+
+ /**
+ * Gets the proposed rotation.
+ *
+ * This method only returns a rotation if the orientation listener is certain
+ * of its proposal. If the rotation is indeterminate, returns -1.
+ *
+ * @return The proposed rotation, or -1 if unknown.
+ */
+ public int getProposedRotation() {
if (mEnabled) {
- return mSensorEventListener.getCurrentRotation();
+ return mSensorEventListener.getProposedRotation();
}
return -1;
}
@@ -137,10 +151,14 @@
/**
* Called when the rotation view of the device has changed.
*
+ * This method is called whenever the orientation becomes certain of an orientation.
+ * It is called each time the orientation determination transitions from being
+ * uncertain to being certain again, even if it is the same orientation as before.
+ *
* @param rotation The new orientation of the device, one of the Surface.ROTATION_* constants.
* @see Surface
*/
- public abstract void onOrientationChanged(int rotation);
+ public abstract void onProposedRotationChanged(int rotation);
/**
* Enables or disables the window orientation listener logging for use with
@@ -182,23 +200,8 @@
* to the corresponding orientation. These thresholds have some hysteresis built-in
* to avoid oscillations between adjacent orientations.
*
- * - Use the magnitude to judge the confidence of the orientation.
- * Under ideal conditions, the magnitude should equal to that of gravity. When it
- * differs significantly, we know the device is under external acceleration and
- * we can't trust the data.
- *
- * - Use the tilt angle to judge the confidence of the orientation.
- * When the tilt angle is high in absolute value then the device is nearly flat
- * so small physical movements produce large changes in orientation angle.
- * This can be the case when the device is being picked up from a table.
- *
- * - Use the orientation angle to judge the confidence of the orientation.
- * The close the orientation angle is to the canonical orientation angle, the better.
- *
- * - Based on the aggregate confidence, we determine how long we want to wait for
- * the new orientation to settle. This is accomplished by integrating the confidence
- * for each orientation over time. When a threshold integration sum is reached
- * then we actually change orientations.
+ * - Wait for the device to settle for a little bit. Once that happens, issue the
+ * new orientation proposal.
*
* Details are explained inline.
*/
@@ -211,22 +214,8 @@
private static final int ACCELEROMETER_DATA_Y = 1;
private static final int ACCELEROMETER_DATA_Z = 2;
- // Rotation constants.
- // These are the same as Surface rotation constants with the addition of a 5th
- // unknown state when we are not confident about the proporsed orientation.
- // One important property of these constants is that they are equal to the
- // orientation angle itself divided by 90. We use this fact to map
- // back and forth between orientation angles and rotation values.
- private static final int ROTATION_UNKNOWN = -1;
- //private static final int ROTATION_0 = Surface.ROTATION_0; // 0
- //private static final int ROTATION_90 = Surface.ROTATION_90; // 1
- //private static final int ROTATION_180 = Surface.ROTATION_180; // 2
- //private static final int ROTATION_270 = Surface.ROTATION_270; // 3
-
private final WindowOrientationListener mOrientationListener;
- private int mRotation = ROTATION_UNKNOWN;
-
/* State for first order low-pass filtering of accelerometer data.
* See http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization for
* signal processing background.
@@ -235,6 +224,24 @@
private long mLastTimestamp = Long.MAX_VALUE; // in nanoseconds
private float mLastFilteredX, mLastFilteredY, mLastFilteredZ;
+ // The current proposal. We wait for the proposal to be stable for a
+ // certain amount of time before accepting it.
+ //
+ // The basic idea is to ignore intermediate poses of the device while the
+ // user is picking up, putting down or turning the device.
+ private int mProposalRotation;
+ private long mProposalAgeMS;
+
+ // A historical trace of tilt and orientation angles. Used to determine whether
+ // the device posture has settled down.
+ private static final int HISTORY_SIZE = 20;
+ private int mHistoryIndex; // index of most recent sample
+ private int mHistoryLength; // length of historical trace
+ private final long[] mHistoryTimestampMS = new long[HISTORY_SIZE];
+ private final float[] mHistoryMagnitudes = new float[HISTORY_SIZE];
+ private final int[] mHistoryTiltAngles = new int[HISTORY_SIZE];
+ private final int[] mHistoryOrientationAngles = new int[HISTORY_SIZE];
+
// The maximum sample inter-arrival time in milliseconds.
// If the acceleration samples are further apart than this amount in time, we reset the
// state of the low-pass filter and orientation properties. This helps to handle
@@ -242,24 +249,26 @@
// a significant gap in samples.
private static final float MAX_FILTER_DELTA_TIME_MS = 1000;
- // The acceleration filter cutoff frequency.
- // This is the frequency at which signals are attenuated by 3dB (half the passband power).
+ // The acceleration filter time constant.
+ //
+ // This time constant is used to tune the acceleration filter such that
+ // impulses and vibrational noise (think car dock) is suppressed before we
+ // try to calculate the tilt and orientation angles.
+ //
+ // The filter time constant is related to the filter cutoff frequency, which is the
+ // frequency at which signals are attenuated by 3dB (half the passband power).
// Each successive octave beyond this frequency is attenuated by an additional 6dB.
//
- // We choose the cutoff frequency such that impulses and vibrational noise
- // (think car dock) is suppressed. However, this filtering does not eliminate
- // all possible sources of orientation ambiguity so we also rely on a dynamic
- // settle time for establishing a new orientation. Filtering adds latency
- // inversely proportional to the cutoff frequency so we don't want to make
- // it too small or we can lose hundreds of milliseconds of responsiveness.
- private static final float FILTER_CUTOFF_FREQUENCY_HZ = 1f;
- private static final float FILTER_TIME_CONSTANT_MS = (float)(500.0f
- / (Math.PI * FILTER_CUTOFF_FREQUENCY_HZ)); // t = 1 / (2pi * Fc) * 1000ms
-
- // The filter gain.
- // We choose a value slightly less than unity to avoid numerical instabilities due
- // to floating-point error accumulation.
- private static final float FILTER_GAIN = 0.999f;
+ // Given a time constant t in seconds, the filter cutoff frequency Fc in Hertz
+ // is given by Fc = 1 / (2pi * t).
+ //
+ // The higher the time constant, the lower the cutoff frequency, so more noise
+ // will be suppressed.
+ //
+ // Filtering adds latency proportional the time constant (inversely proportional
+ // to the cutoff frequency) so we don't want to make the time constant too
+ // large or we can lose responsiveness.
+ private static final float FILTER_TIME_CONSTANT_MS = 100.0f;
/* State for orientation detection. */
@@ -297,10 +306,10 @@
// The ideal tilt angle is 0 (when the device is vertical) so the limits establish
// how close to vertical the device must be in order to change orientation.
private static final int[][] TILT_TOLERANCE = new int[][] {
- /* ROTATION_0 */ { -20, 75 },
- /* ROTATION_90 */ { -20, 70 },
- /* ROTATION_180 */ { -20, 65 },
- /* ROTATION_270 */ { -20, 70 }
+ /* ROTATION_0 */ { -20, 70 },
+ /* ROTATION_90 */ { -20, 60 },
+ /* ROTATION_180 */ { -20, 50 },
+ /* ROTATION_270 */ { -20, 60 }
};
// The gap angle in degrees between adjacent orientation angles for hysteresis.
@@ -308,41 +317,31 @@
// adjacent orientation. No orientation proposal is made when the orientation
// angle is within the gap between the current orientation and the adjacent
// orientation.
- private static final int ADJACENT_ORIENTATION_ANGLE_GAP = 30;
+ private static final int ADJACENT_ORIENTATION_ANGLE_GAP = 45;
- // The confidence scale factors for angle, tilt and magnitude.
- // When the distance between the actual value and the ideal value is the
- // specified delta, orientation transitions will take twice as long as they would
- // in the ideal case. Increasing or decreasing the delta has an exponential effect
- // on each factor's influence over the transition time.
+ // The number of milliseconds for which the device posture must be stable
+ // before we perform an orientation change. If the device appears to be rotating
+ // (being picked up, put down) then we keep waiting until it settles.
+ private static final int SETTLE_TIME_MS = 200;
- // Transition takes 2x longer when angle is 30 degrees from ideal orientation angle.
- private static final float ORIENTATION_ANGLE_CONFIDENCE_SCALE =
- confidenceScaleFromDelta(30);
+ // The maximum change in magnitude that can occur during the settle time.
+ // Tuning this constant particularly helps to filter out situations where the
+ // device is being picked up or put down by the user.
+ private static final float SETTLE_MAGNITUDE_MAX_DELTA =
+ SensorManager.STANDARD_GRAVITY * 0.2f;
- // Transition takes 2x longer when tilt is 60 degrees from vertical.
- private static final float TILT_ANGLE_CONFIDENCE_SCALE = confidenceScaleFromDelta(60);
+ // The maximum change in tilt angle that can occur during the settle time.
+ private static final int SETTLE_TILT_ANGLE_MAX_DELTA = 5;
- // Transition takes 2x longer when acceleration is 0.5 Gs.
- private static final float MAGNITUDE_CONFIDENCE_SCALE = confidenceScaleFromDelta(
- SensorManager.STANDARD_GRAVITY * 0.5f);
-
- // The number of milliseconds for which a new orientation must be stable before
- // we perform an orientation change under ideal conditions. It will take
- // proportionally longer than this to effect an orientation change when
- // the proposed orientation confidence is low.
- private static final float ORIENTATION_SETTLE_TIME_MS = 250;
-
- // The confidence that we have abount effecting each orientation change.
- // When one of these values exceeds 1.0, we have determined our new orientation!
- private float mConfidence[] = new float[4];
+ // The maximum change in orientation angle that can occur during the settle time.
+ private static final int SETTLE_ORIENTATION_ANGLE_MAX_DELTA = 5;
public SensorEventListenerImpl(WindowOrientationListener orientationListener) {
mOrientationListener = orientationListener;
}
- public int getCurrentRotation() {
- return mRotation; // may be -1, if unknown
+ public int getProposedRotation() {
+ return mProposalAgeMS >= SETTLE_TIME_MS ? mProposalRotation : -1;
}
@Override
@@ -368,20 +367,18 @@
// Reset the orientation listener state if the samples are too far apart in time
// or when we see values of (0, 0, 0) which indicates that we polled the
// accelerometer too soon after turning it on and we don't have any data yet.
- final float timeDeltaMS = (event.timestamp - mLastTimestamp) * 0.000001f;
+ final long now = event.timestamp;
+ final float timeDeltaMS = (now - mLastTimestamp) * 0.000001f;
boolean skipSample;
if (timeDeltaMS <= 0 || timeDeltaMS > MAX_FILTER_DELTA_TIME_MS
|| (x == 0 && y == 0 && z == 0)) {
if (log) {
Slog.v(TAG, "Resetting orientation listener.");
}
- for (int i = 0; i < 4; i++) {
- mConfidence[i] = 0;
- }
+ clearProposal();
skipSample = true;
} else {
- final float alpha = timeDeltaMS
- / (FILTER_TIME_CONSTANT_MS + timeDeltaMS) * FILTER_GAIN;
+ final float alpha = timeDeltaMS / (FILTER_TIME_CONSTANT_MS + timeDeltaMS);
x = alpha * (x - mLastFilteredX) + mLastFilteredX;
y = alpha * (y - mLastFilteredY) + mLastFilteredY;
z = alpha * (z - mLastFilteredZ) + mLastFilteredZ;
@@ -391,17 +388,13 @@
}
skipSample = false;
}
- mLastTimestamp = event.timestamp;
+ mLastTimestamp = now;
mLastFilteredX = x;
mLastFilteredY = y;
mLastFilteredZ = z;
- boolean orientationChanged = false;
+ final int oldProposedRotation = getProposedRotation();
if (!skipSample) {
- // Determine a proposed orientation based on the currently available data.
- int proposedOrientation = ROTATION_UNKNOWN;
- float combinedConfidence = 1.0f;
-
// Calculate the magnitude of the acceleration vector.
final float magnitude = (float) Math.sqrt(x * x + y * y + z * z);
if (magnitude < MIN_ACCELERATION_MAGNITUDE
@@ -410,6 +403,7 @@
Slog.v(TAG, "Ignoring sensor data, magnitude out of range: "
+ "magnitude=" + magnitude);
}
+ clearProposal();
} else {
// Calculate the tilt angle.
// This is the angle between the up vector and the x-y plane (the plane of
@@ -417,123 +411,82 @@
// -90 degrees: screen horizontal and facing the ground (overhead)
// 0 degrees: screen vertical
// 90 degrees: screen horizontal and facing the sky (on table)
- final int tiltAngle = (int) Math.round(
- Math.asin(z / magnitude) * RADIANS_TO_DEGREES);
+ final int tiltAngle = (int) Math.round(
+ Math.asin(z / magnitude) * RADIANS_TO_DEGREES);
- // If the tilt angle is too close to horizontal then we cannot determine
- // the orientation angle of the screen.
- if (Math.abs(tiltAngle) > MAX_TILT) {
- if (log) {
- Slog.v(TAG, "Ignoring sensor data, tilt angle too high: "
- + "magnitude=" + magnitude + ", tiltAngle=" + tiltAngle);
- }
- } else {
- // Calculate the orientation angle.
- // This is the angle between the x-y projection of the up vector onto
- // the +y-axis, increasing clockwise in a range of [0, 360] degrees.
- int orientationAngle = (int) Math.round(
- -Math.atan2(-x, y) * RADIANS_TO_DEGREES);
- if (orientationAngle < 0) {
- // atan2 returns [-180, 180]; normalize to [0, 360]
- orientationAngle += 360;
- }
-
- // Find the nearest orientation.
- // An orientation of 0 can have a nearest angle of 0 or 360 depending
- // on which is closer to the measured orientation angle. We leave the
- // nearest angle at 360 in that case since it makes the delta calculation
- // for orientation angle confidence easier below.
- int nearestOrientation = (orientationAngle + 45) / 90;
- int nearestOrientationAngle = nearestOrientation * 90;
- if (nearestOrientation == 4) {
- nearestOrientation = 0;
- }
-
- // Determine the proposed orientation.
- // The confidence of the proposal is 1.0 when it is ideal and it
- // decays exponentially as the proposal moves further from the ideal
- // angle, tilt and magnitude of the proposed orientation.
- if (isTiltAngleAcceptable(nearestOrientation, tiltAngle)
- && isOrientationAngleAcceptable(nearestOrientation,
- orientationAngle)) {
- proposedOrientation = nearestOrientation;
-
- final float idealOrientationAngle = nearestOrientationAngle;
- final float orientationConfidence = confidence(orientationAngle,
- idealOrientationAngle, ORIENTATION_ANGLE_CONFIDENCE_SCALE);
-
- final float idealTiltAngle = 0;
- final float tiltConfidence = confidence(tiltAngle,
- idealTiltAngle, TILT_ANGLE_CONFIDENCE_SCALE);
-
- final float idealMagnitude = SensorManager.STANDARD_GRAVITY;
- final float magnitudeConfidence = confidence(magnitude,
- idealMagnitude, MAGNITUDE_CONFIDENCE_SCALE);
-
- combinedConfidence = orientationConfidence
- * tiltConfidence * magnitudeConfidence;
-
- if (log) {
- Slog.v(TAG, "Proposal: "
- + "magnitude=" + magnitude
- + ", tiltAngle=" + tiltAngle
- + ", orientationAngle=" + orientationAngle
- + ", proposedOrientation=" + proposedOrientation
- + ", combinedConfidence=" + combinedConfidence
- + ", orientationConfidence=" + orientationConfidence
- + ", tiltConfidence=" + tiltConfidence
- + ", magnitudeConfidence=" + magnitudeConfidence);
- }
- } else {
- if (log) {
- Slog.v(TAG, "Ignoring sensor data, no proposal: "
- + "magnitude=" + magnitude + ", tiltAngle=" + tiltAngle
- + ", orientationAngle=" + orientationAngle);
- }
- }
- }
- }
-
- // Sum up the orientation confidence weights.
- // Detect an orientation change when the sum reaches 1.0.
- final float confidenceAmount = combinedConfidence * timeDeltaMS
- / ORIENTATION_SETTLE_TIME_MS;
- for (int i = 0; i < 4; i++) {
- if (i == proposedOrientation) {
- mConfidence[i] += confidenceAmount;
- if (mConfidence[i] >= 1.0f) {
- mConfidence[i] = 1.0f;
-
- if (i != mRotation) {
- if (log) {
- Slog.v(TAG, "Orientation changed! rotation=" + i);
- }
- mRotation = i;
- orientationChanged = true;
- }
+ // If the tilt angle is too close to horizontal then we cannot determine
+ // the orientation angle of the screen.
+ if (Math.abs(tiltAngle) > MAX_TILT) {
+ if (log) {
+ Slog.v(TAG, "Ignoring sensor data, tilt angle too high: "
+ + "magnitude=" + magnitude + ", tiltAngle=" + tiltAngle);
}
+ clearProposal();
} else {
- mConfidence[i] -= confidenceAmount;
- if (mConfidence[i] < 0.0f) {
- mConfidence[i] = 0.0f;
+ // Calculate the orientation angle.
+ // This is the angle between the x-y projection of the up vector onto
+ // the +y-axis, increasing clockwise in a range of [0, 360] degrees.
+ int orientationAngle = (int) Math.round(
+ -Math.atan2(-x, y) * RADIANS_TO_DEGREES);
+ if (orientationAngle < 0) {
+ // atan2 returns [-180, 180]; normalize to [0, 360]
+ orientationAngle += 360;
+ }
+
+ // Find the nearest rotation.
+ int nearestRotation = (orientationAngle + 45) / 90;
+ if (nearestRotation == 4) {
+ nearestRotation = 0;
+ }
+
+ // Determine the proposed orientation.
+ // The confidence of the proposal is 1.0 when it is ideal and it
+ // decays exponentially as the proposal moves further from the ideal
+ // angle, tilt and magnitude of the proposed orientation.
+ if (!isTiltAngleAcceptable(nearestRotation, tiltAngle)
+ || !isOrientationAngleAcceptable(nearestRotation,
+ orientationAngle)) {
+ if (log) {
+ Slog.v(TAG, "Ignoring sensor data, no proposal: "
+ + "magnitude=" + magnitude + ", tiltAngle=" + tiltAngle
+ + ", orientationAngle=" + orientationAngle);
+ }
+ clearProposal();
+ } else {
+ if (log) {
+ Slog.v(TAG, "Proposal: "
+ + "magnitude=" + magnitude
+ + ", tiltAngle=" + tiltAngle
+ + ", orientationAngle=" + orientationAngle
+ + ", proposalRotation=" + mProposalRotation);
+ }
+ updateProposal(nearestRotation, now / 1000000L,
+ magnitude, tiltAngle, orientationAngle);
}
}
}
}
// Write final statistics about where we are in the orientation detection process.
+ final int proposedRotation = getProposedRotation();
if (log) {
- Slog.v(TAG, "Result: rotation=" + mRotation
- + ", confidence=["
- + mConfidence[0] + ", "
- + mConfidence[1] + ", "
- + mConfidence[2] + ", "
- + mConfidence[3] + "], timeDeltaMS=" + timeDeltaMS);
+ final float proposalConfidence = Math.min(
+ mProposalAgeMS * 1.0f / SETTLE_TIME_MS, 1.0f);
+ Slog.v(TAG, "Result: currentRotation=" + mOrientationListener.mCurrentRotation
+ + ", proposedRotation=" + proposedRotation
+ + ", timeDeltaMS=" + timeDeltaMS
+ + ", proposalRotation=" + mProposalRotation
+ + ", proposalAgeMS=" + mProposalAgeMS
+ + ", proposalConfidence=" + proposalConfidence);
}
// Tell the listener.
- if (orientationChanged) {
- mOrientationListener.onOrientationChanged(mRotation);
+ if (proposedRotation != oldProposedRotation && proposedRotation >= 0) {
+ if (log) {
+ Slog.v(TAG, "Proposed rotation changed! proposedRotation=" + proposedRotation
+ + ", oldProposedRotation=" + oldProposedRotation);
+ }
+ mOrientationListener.onProposedRotationChanged(proposedRotation);
}
}
@@ -541,33 +494,34 @@
* Returns true if the tilt angle is acceptable for a proposed
* orientation transition.
*/
- private boolean isTiltAngleAcceptable(int proposedOrientation,
+ private boolean isTiltAngleAcceptable(int proposedRotation,
int tiltAngle) {
- return tiltAngle >= TILT_TOLERANCE[proposedOrientation][0]
- && tiltAngle <= TILT_TOLERANCE[proposedOrientation][1];
+ return tiltAngle >= TILT_TOLERANCE[proposedRotation][0]
+ && tiltAngle <= TILT_TOLERANCE[proposedRotation][1];
}
/**
* Returns true if the orientation angle is acceptable for a proposed
* orientation transition.
+ *
* This function takes into account the gap between adjacent orientations
* for hysteresis.
*/
- private boolean isOrientationAngleAcceptable(int proposedOrientation,
- int orientationAngle) {
- final int currentOrientation = mRotation;
-
+ private boolean isOrientationAngleAcceptable(int proposedRotation, int orientationAngle) {
// If there is no current rotation, then there is no gap.
- if (currentOrientation != ROTATION_UNKNOWN) {
- // If the proposed orientation is the same or is counter-clockwise adjacent,
+ // The gap is used only to introduce hysteresis among advertised orientation
+ // changes to avoid flapping.
+ final int currentRotation = mOrientationListener.mCurrentRotation;
+ if (currentRotation >= 0) {
+ // If the proposed rotation is the same or is counter-clockwise adjacent,
// then we set a lower bound on the orientation angle.
- // For example, if currentOrientation is ROTATION_0 and proposed is ROTATION_90,
+ // For example, if currentRotation is ROTATION_0 and proposed is ROTATION_90,
// then we want to check orientationAngle > 45 + GAP / 2.
- if (proposedOrientation == currentOrientation
- || proposedOrientation == (currentOrientation + 1) % 4) {
- int lowerBound = proposedOrientation * 90 - 45
+ if (proposedRotation == currentRotation
+ || proposedRotation == (currentRotation + 1) % 4) {
+ int lowerBound = proposedRotation * 90 - 45
+ ADJACENT_ORIENTATION_ANGLE_GAP / 2;
- if (proposedOrientation == 0) {
+ if (proposedRotation == 0) {
if (orientationAngle >= 315 && orientationAngle < lowerBound + 360) {
return false;
}
@@ -578,15 +532,15 @@
}
}
- // If the proposed orientation is the same or is clockwise adjacent,
+ // If the proposed rotation is the same or is clockwise adjacent,
// then we set an upper bound on the orientation angle.
- // For example, if currentOrientation is ROTATION_0 and proposed is ROTATION_270,
+ // For example, if currentRotation is ROTATION_0 and proposed is ROTATION_270,
// then we want to check orientationAngle < 315 - GAP / 2.
- if (proposedOrientation == currentOrientation
- || proposedOrientation == (currentOrientation + 3) % 4) {
- int upperBound = proposedOrientation * 90 + 45
+ if (proposedRotation == currentRotation
+ || proposedRotation == (currentRotation + 3) % 4) {
+ int upperBound = proposedRotation * 90 + 45
- ADJACENT_ORIENTATION_ANGLE_GAP / 2;
- if (proposedOrientation == 0) {
+ if (proposedRotation == 0) {
if (orientationAngle <= 45 && orientationAngle > upperBound) {
return false;
}
@@ -600,21 +554,58 @@
return true;
}
- /**
- * Calculate an exponentially weighted confidence value in the range [0.0, 1.0].
- * The further the value is from the target, the more the confidence trends to 0.
- */
- private static float confidence(float value, float target, float scale) {
- return (float) Math.exp(-Math.abs(value - target) * scale);
+ private void clearProposal() {
+ mProposalRotation = -1;
+ mProposalAgeMS = 0;
}
- /**
- * Calculate a scale factor for the confidence weight exponent.
- * The scale value is chosen such that confidence(value, target, scale) == 0.5
- * whenever abs(value - target) == cutoffDelta.
- */
- private static float confidenceScaleFromDelta(float cutoffDelta) {
- return (float) -Math.log(0.5) / cutoffDelta;
+ private void updateProposal(int rotation, long timestampMS,
+ float magnitude, int tiltAngle, int orientationAngle) {
+ if (mProposalRotation != rotation) {
+ mProposalRotation = rotation;
+ mHistoryIndex = 0;
+ mHistoryLength = 0;
+ }
+
+ final int index = mHistoryIndex;
+ mHistoryTimestampMS[index] = timestampMS;
+ mHistoryMagnitudes[index] = magnitude;
+ mHistoryTiltAngles[index] = tiltAngle;
+ mHistoryOrientationAngles[index] = orientationAngle;
+ mHistoryIndex = (index + 1) % HISTORY_SIZE;
+ if (mHistoryLength < HISTORY_SIZE) {
+ mHistoryLength += 1;
+ }
+
+ long age = 0;
+ for (int i = 1; i < mHistoryLength; i++) {
+ final int olderIndex = (index + HISTORY_SIZE - i) % HISTORY_SIZE;
+ if (Math.abs(mHistoryMagnitudes[olderIndex] - magnitude)
+ > SETTLE_MAGNITUDE_MAX_DELTA) {
+ break;
+ }
+ if (angleAbsoluteDelta(mHistoryTiltAngles[olderIndex],
+ tiltAngle) > SETTLE_TILT_ANGLE_MAX_DELTA) {
+ break;
+ }
+ if (angleAbsoluteDelta(mHistoryOrientationAngles[olderIndex],
+ orientationAngle) > SETTLE_ORIENTATION_ANGLE_MAX_DELTA) {
+ break;
+ }
+ age = timestampMS - mHistoryTimestampMS[olderIndex];
+ if (age >= SETTLE_TIME_MS) {
+ break;
+ }
+ }
+ mProposalAgeMS = age;
+ }
+
+ private static int angleAbsoluteDelta(int a, int b) {
+ int delta = Math.abs(a - b);
+ if (delta > 180) {
+ delta = 360 - delta;
+ }
+ return delta;
}
}
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 15180ca..139f9f3 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -4302,6 +4302,7 @@
selectionDone();
}
mOrientation = newConfig.orientation;
+ contentInvalidateAll();
}
/**
@@ -9282,13 +9283,13 @@
private static void checkThread() {
if (Looper.myLooper() != Looper.getMainLooper()) {
- RuntimeException exception = new RuntimeException(
- "A WebView method was called on thread '" +
+ Throwable throwable = new Throwable(
+ "Warning: A WebView method was called on thread '" +
Thread.currentThread().getName() + "'. " +
"All WebView methods must be called on the UI thread. " +
"Future versions of WebView may not support use on other threads.");
- Log.e(LOGTAG, Log.getStackTraceString(exception));
- StrictMode.onWebViewMethodCalledOnWrongThread(exception);
+ Log.w(LOGTAG, Log.getStackTraceString(throwable));
+ StrictMode.onWebViewMethodCalledOnWrongThread(throwable);
}
}
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index f230031..adf2b7b 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -120,6 +120,7 @@
private CharSequence mOldQueryText;
private CharSequence mUserQuery;
private boolean mExpandedInActionView;
+ private int mCollapsedImeOptions;
private SearchableInfo mSearchable;
private Bundle mAppSearchData;
@@ -1166,6 +1167,7 @@
clearFocus();
updateViewsVisibility(true);
mQueryTextView.setText("");
+ mQueryTextView.setImeOptions(mCollapsedImeOptions);
mExpandedInActionView = false;
}
@@ -1175,6 +1177,8 @@
@Override
public void onActionViewExpanded() {
mExpandedInActionView = true;
+ mCollapsedImeOptions = mQueryTextView.getImeOptions();
+ mQueryTextView.setImeOptions(mCollapsedImeOptions | EditorInfo.IME_FLAG_NO_FULLSCREEN);
setIconified(false);
}
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index 14cbf6f..6b2f3e4 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -75,6 +75,20 @@
mLength = 0;
}
+ /**
+ * @return true if a spell checker session has successfully been created. Returns false if not,
+ * for instance when spell checking has been disabled in settings.
+ */
+ public boolean isSessionActive() {
+ return mSpellCheckerSession != null;
+ }
+
+ public void closeSession() {
+ if (mSpellCheckerSession != null) {
+ mSpellCheckerSession.close();
+ }
+ }
+
public void addSpellCheckSpan(SpellCheckSpan spellCheckSpan) {
int length = mIds.length;
if (mLength >= length) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 8db6592..8ea55c6 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -3250,7 +3250,8 @@
sendOnTextChanged(text, 0, oldlen, textLength);
onTextChanged(text, 0, oldlen, textLength);
- if (startSpellCheck) {
+ if (startSpellCheck && mSpellChecker != null) {
+ // This view has to have been previously attached for mSpellChecker to exist
updateSpellCheckSpans(0, textLength);
}
@@ -4412,6 +4413,8 @@
// Resolve drawables as the layout direction has been resolved
resolveDrawables();
+
+ updateSpellCheckSpans(0, mText.length());
}
@Override
@@ -4443,6 +4446,14 @@
hideControllers();
resetResolvedDrawables();
+
+ if (mSpellChecker != null) {
+ mSpellChecker.closeSession();
+ removeMisspelledSpans();
+ // Forces the creation of a new SpellChecker next time this window is created.
+ // Will handle the cases where the settings has been changed in the meantime.
+ mSpellChecker = null;
+ }
}
@Override
@@ -6130,7 +6141,7 @@
TruncateAt effectiveEllipsize = mEllipsize;
if (mEllipsize == TruncateAt.MARQUEE &&
mMarqueeFadeMode == MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) {
- effectiveEllipsize = TruncateAt.END;
+ effectiveEllipsize = TruncateAt.END_SMALL;
}
if (mTextDir == null) {
@@ -7595,7 +7606,7 @@
}
ims.mChangedDelta += after-before;
}
-
+
sendOnTextChanged(buffer, start, before, after);
onTextChanged(buffer, start, before, after);
@@ -7737,7 +7748,8 @@
* Create new SpellCheckSpans on the modified region.
*/
private void updateSpellCheckSpans(int start, int end) {
- if (!(mText instanceof Editable) || !isSuggestionsEnabled()) return;
+ if (!isTextEditable() || !isSuggestionsEnabled() || !getSpellChecker().isSessionActive())
+ return;
Editable text = (Editable) mText;
WordIterator wordIterator = getWordIterator();
@@ -8427,13 +8439,31 @@
int flags = suggestionSpans[i].getFlags();
if ((flags & SuggestionSpan.FLAG_EASY_CORRECT) != 0
&& (flags & SuggestionSpan.FLAG_MISSPELLED) == 0) {
- flags = flags & ~SuggestionSpan.FLAG_EASY_CORRECT;
+ flags &= ~SuggestionSpan.FLAG_EASY_CORRECT;
suggestionSpans[i].setFlags(flags);
}
}
}
}
+ /**
+ * Removes the suggestion spans for misspelled words.
+ */
+ private void removeMisspelledSpans() {
+ if (mText instanceof Spannable) {
+ Spannable spannable = (Spannable) mText;
+ SuggestionSpan[] suggestionSpans = spannable.getSpans(0,
+ spannable.length(), SuggestionSpan.class);
+ for (int i = 0; i < suggestionSpans.length; i++) {
+ int flags = suggestionSpans[i].getFlags();
+ if ((flags & SuggestionSpan.FLAG_EASY_CORRECT) != 0
+ && (flags & SuggestionSpan.FLAG_MISSPELLED) != 0) {
+ spannable.removeSpan(suggestionSpans[i]);
+ }
+ }
+ }
+ }
+
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
if (mMovement != null && mText instanceof Spannable && mLayout != null) {
@@ -9573,7 +9603,6 @@
private final Comparator<SuggestionSpan> mSuggestionSpanComparator;
private final HashMap<SuggestionSpan, Integer> mSpansLengths;
-
private class CustomPopupWindow extends PopupWindow {
public CustomPopupWindow(Context context, int defStyle) {
super(context, null, defStyle);
@@ -9585,9 +9614,8 @@
TextView.this.getPositionListener().removeSubscriber(SuggestionsPopupWindow.this);
- if ((mText instanceof Spannable)) {
- ((Spannable) mText).removeSpan(mSuggestionRangeSpan);
- }
+ // Safe cast since show() checks that mText is an Editable
+ ((Spannable) mText).removeSpan(mSuggestionRangeSpan);
setCursorVisible(mCursorWasVisibleBeforeSuggestions);
if (hasInsertionController()) {
@@ -9637,8 +9665,8 @@
void removeMisspelledFlag() {
int suggestionSpanFlags = suggestionSpan.getFlags();
if ((suggestionSpanFlags & SuggestionSpan.FLAG_MISSPELLED) > 0) {
- suggestionSpanFlags &= ~(SuggestionSpan.FLAG_MISSPELLED);
- suggestionSpanFlags &= ~(SuggestionSpan.FLAG_EASY_CORRECT);
+ suggestionSpanFlags &= ~SuggestionSpan.FLAG_MISSPELLED;
+ suggestionSpanFlags &= ~SuggestionSpan.FLAG_EASY_CORRECT;
suggestionSpan.setFlags(suggestionSpanFlags);
}
}
@@ -9887,6 +9915,8 @@
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ hide();
+
if (view instanceof TextView) {
TextView textView = (TextView) view;
Editable editable = (Editable) mText;
@@ -9894,6 +9924,7 @@
SuggestionInfo suggestionInfo = mSuggestionInfos[position];
final int spanStart = editable.getSpanStart(suggestionInfo.suggestionSpan);
final int spanEnd = editable.getSpanEnd(suggestionInfo.suggestionSpan);
+ if (spanStart < 0 || spanEnd < 0) return; // Span has been removed
final String originalText = mText.subSequence(spanStart, spanEnd).toString();
if (suggestionInfo.suggestionIndex < 0) {
@@ -9955,7 +9986,6 @@
Selection.setSelection(editable, spanEnd + lengthDifference);
}
}
- hide();
}
}
@@ -10520,9 +10550,7 @@
public abstract int getCurrentCursorOffset();
- protected void updateSelection(int offset) {
- updateDrawable();
- }
+ protected abstract void updateSelection(int offset);
public abstract void updatePosition(float x, float y);
@@ -10796,8 +10824,8 @@
@Override
public void updateSelection(int offset) {
- super.updateSelection(offset);
Selection.setSelection((Spannable) mText, offset, getSelectionEnd());
+ updateDrawable();
}
@Override
@@ -10838,8 +10866,8 @@
@Override
public void updateSelection(int offset) {
- super.updateSelection(offset);
Selection.setSelection((Spannable) mText, getSelectionStart(), offset);
+ updateDrawable();
}
@Override
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 159929b..3d22929 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -133,4 +133,13 @@
}
return false;
}
+
+ public static boolean contains(int[] array, int value) {
+ for (int element : array) {
+ if (element == value) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
index 0db1ccc..aabea2c 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
@@ -200,8 +200,19 @@
}
}
- final boolean hasOverflow = mReserveOverflow && mMenu != null &&
- mMenu.getNonActionItems().size() > 0;
+ final ArrayList<MenuItemImpl> nonActionItems = mMenu != null ?
+ mMenu.getNonActionItems() : null;
+
+ boolean hasOverflow = false;
+ if (mReserveOverflow && nonActionItems != null) {
+ final int count = nonActionItems.size();
+ if (count == 1) {
+ hasOverflow = !nonActionItems.get(0).isActionViewExpanded();
+ } else {
+ hasOverflow = count > 0;
+ }
+ }
+
if (hasOverflow) {
if (mOverflowButton == null) {
mOverflowButton = new OverflowMenuButton(mContext);
diff --git a/core/java/com/android/internal/view/menu/ListMenuPresenter.java b/core/java/com/android/internal/view/menu/ListMenuPresenter.java
index 0141427..a331bec 100644
--- a/core/java/com/android/internal/view/menu/ListMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ListMenuPresenter.java
@@ -17,6 +17,7 @@
package com.android.internal.view.menu;
import android.content.Context;
+import android.database.DataSetObserver;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.SparseArray;
@@ -47,7 +48,7 @@
int mItemLayoutRes;
private Callback mCallback;
- private MenuAdapter mAdapter;
+ MenuAdapter mAdapter;
private int mId;
@@ -216,14 +217,29 @@
}
private class MenuAdapter extends BaseAdapter {
+ private int mExpandedIndex = -1;
+
+ public MenuAdapter() {
+ registerDataSetObserver(new ExpandedIndexObserver());
+ findExpandedIndex();
+ }
+
public int getCount() {
ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
- return items.size() - mItemIndexOffset;
+ int count = items.size() - mItemIndexOffset;
+ if (mExpandedIndex < 0) {
+ return count;
+ }
+ return count - 1;
}
public MenuItemImpl getItem(int position) {
ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
- return items.get(position + mItemIndexOffset);
+ position += mItemIndexOffset;
+ if (mExpandedIndex >= 0 && position >= mExpandedIndex) {
+ position++;
+ }
+ return items.get(position);
}
public long getItemId(int position) {
@@ -241,5 +257,28 @@
itemView.initialize(getItem(position), 0);
return convertView;
}
+
+ void findExpandedIndex() {
+ final MenuItemImpl expandedItem = mMenu.getExpandedItem();
+ if (expandedItem != null) {
+ final ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
+ final int count = items.size();
+ for (int i = 0; i < count; i++) {
+ final MenuItemImpl item = items.get(i);
+ if (item == expandedItem) {
+ mExpandedIndex = i;
+ return;
+ }
+ }
+ }
+ mExpandedIndex = -1;
+ }
+ }
+
+ private class ExpandedIndexObserver extends DataSetObserver {
+ @Override
+ public void onChanged() {
+ mAdapter.findExpandedIndex();
+ }
}
}
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index c30e83b..9fbca82 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -1258,4 +1258,8 @@
}
return collapsed;
}
+
+ public MenuItemImpl getExpandedItem() {
+ return mExpandedItem;
+ }
}
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index 6265618..329b457 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.res.Resources;
+import android.database.DataSetObserver;
import android.os.Parcelable;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -286,22 +287,45 @@
return false;
}
+ @Override
+ public int getId() {
+ return 0;
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ return null;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ }
+
private class MenuAdapter extends BaseAdapter {
private MenuBuilder mAdapterMenu;
+ private int mExpandedIndex = -1;
public MenuAdapter(MenuBuilder menu) {
mAdapterMenu = menu;
+ registerDataSetObserver(new ExpandedIndexObserver());
+ findExpandedIndex();
}
public int getCount() {
ArrayList<MenuItemImpl> items = mOverflowOnly ?
mAdapterMenu.getNonActionItems() : mAdapterMenu.getVisibleItems();
- return items.size();
+ if (mExpandedIndex < 0) {
+ return items.size();
+ }
+ return items.size() - 1;
}
public MenuItemImpl getItem(int position) {
ArrayList<MenuItemImpl> items = mOverflowOnly ?
mAdapterMenu.getNonActionItems() : mAdapterMenu.getVisibleItems();
+ if (mExpandedIndex >= 0 && position >= mExpandedIndex) {
+ position++;
+ }
return items.get(position);
}
@@ -323,19 +347,28 @@
itemView.initialize(getItem(position), 0);
return convertView;
}
+
+ void findExpandedIndex() {
+ final MenuItemImpl expandedItem = mMenu.getExpandedItem();
+ if (expandedItem != null) {
+ final ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
+ final int count = items.size();
+ for (int i = 0; i < count; i++) {
+ final MenuItemImpl item = items.get(i);
+ if (item == expandedItem) {
+ mExpandedIndex = i;
+ return;
+ }
+ }
+ }
+ mExpandedIndex = -1;
+ }
}
- @Override
- public int getId() {
- return 0;
- }
-
- @Override
- public Parcelable onSaveInstanceState() {
- return null;
- }
-
- @Override
- public void onRestoreInstanceState(Parcelable state) {
+ private class ExpandedIndexObserver extends DataSetObserver {
+ @Override
+ public void onChanged() {
+ mAdapter.findExpandedIndex();
+ }
}
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 19dc42b..81e7c34 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -947,10 +947,15 @@
*
* If there's currently a call in progress, the button will take them to the call
* @param button the button to update
+ * @param the phone state:
+ * {@link TelephonyManager#CALL_STATE_IDLE}
+ * {@link TelephonyManager#CALL_STATE_RINGING}
+ * {@link TelephonyManager#CALL_STATE_OFFHOOK}
* @param showIfCapable indicates whether the button should be shown if emergency calls are
* possible on the device
*/
- public void updateEmergencyCallButtonState(Button button, boolean showIfCapable) {
+ public void updateEmergencyCallButtonState(Button button, int phoneState,
+ boolean showIfCapable) {
if (isEmergencyCallCapable() && showIfCapable) {
button.setVisibility(View.VISIBLE);
} else {
@@ -958,9 +963,8 @@
return;
}
- int newState = TelephonyManager.getDefault().getCallState();
int textId;
- if (newState == TelephonyManager.CALL_STATE_OFFHOOK) {
+ if (phoneState == TelephonyManager.CALL_STATE_OFFHOOK) {
// show "return to call" text and show phone icon
textId = R.string.lockscreen_return_to_call;
int phoneCallIcon = R.drawable.stat_sys_phone_call;
@@ -990,22 +994,4 @@
}
return false;
}
-
- /**
- * Performs concentenation of PLMN/SPN
- * @param plmn
- * @param spn
- * @return
- */
- public static CharSequence getCarrierString(CharSequence plmn, CharSequence spn) {
- if (plmn != null && spn == null) {
- return plmn;
- } else if (plmn != null && spn != null) {
- return plmn + "|" + spn;
- } else if (plmn == null && spn != null) {
- return spn;
- } else {
- return "";
- }
- }
}
diff --git a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
index 1b5112f..ebd355a 100644
--- a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
+++ b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
@@ -428,7 +428,7 @@
"x", mWaveCenterX,
"y", mWaveCenterY,
"onUpdate", mUpdateListener,
- "onComplete", mResetListenerWithPing);
+ "onComplete", mDragging ? mResetListenerWithPing : mResetListener);
}
setGrabbedState(OnTriggerListener.NO_HANDLE);
diff --git a/core/res/res/drawable-hdpi/indicator_code_lock_drag_direction_green_up.png b/core/res/res/drawable-hdpi/indicator_code_lock_drag_direction_green_up.png
deleted file mode 100644
index 6560696..0000000
--- a/core/res/res/drawable-hdpi/indicator_code_lock_drag_direction_green_up.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_wifi_in_range.png b/core/res/res/drawable-hdpi/stat_notify_wifi_in_range.png
index 716ba9d..f3aaa27 100644
--- a/core/res/res/drawable-hdpi/stat_notify_wifi_in_range.png
+++ b/core/res/res/drawable-hdpi/stat_notify_wifi_in_range.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/vpn_connected.png b/core/res/res/drawable-hdpi/vpn_connected.png
index 65fc6db..c3547e8 100644
--- a/core/res/res/drawable-hdpi/vpn_connected.png
+++ b/core/res/res/drawable-hdpi/vpn_connected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/vpn_disconnected.png b/core/res/res/drawable-hdpi/vpn_disconnected.png
index 2440c69..10a9065 100644
--- a/core/res/res/drawable-hdpi/vpn_disconnected.png
+++ b/core/res/res/drawable-hdpi/vpn_disconnected.png
Binary files differ
diff --git a/core/res/res/drawable-large-mdpi/indicator_code_lock_drag_direction_green_up.png b/core/res/res/drawable-large-mdpi/indicator_code_lock_drag_direction_green_up.png
deleted file mode 100644
index 0bc86c3..0000000
--- a/core/res/res/drawable-large-mdpi/indicator_code_lock_drag_direction_green_up.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-ldpi/indicator_code_lock_drag_direction_green_up.png b/core/res/res/drawable-ldpi/indicator_code_lock_drag_direction_green_up.png
deleted file mode 100644
index 9c8610f..0000000
--- a/core/res/res/drawable-ldpi/indicator_code_lock_drag_direction_green_up.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_green_up.png b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_green_up.png
deleted file mode 100644
index 7ddeba5..0000000
--- a/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_green_up.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_wifi_in_range.png b/core/res/res/drawable-mdpi/stat_notify_wifi_in_range.png
index 3bf0d35..747cb97 100644
--- a/core/res/res/drawable-mdpi/stat_notify_wifi_in_range.png
+++ b/core/res/res/drawable-mdpi/stat_notify_wifi_in_range.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/vpn_connected.png b/core/res/res/drawable-mdpi/vpn_connected.png
index 0d1a026..7e167f8 100644
--- a/core/res/res/drawable-mdpi/vpn_connected.png
+++ b/core/res/res/drawable-mdpi/vpn_connected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/vpn_disconnected.png b/core/res/res/drawable-mdpi/vpn_disconnected.png
index d16d7fb..a08c42a 100644
--- a/core/res/res/drawable-mdpi/vpn_disconnected.png
+++ b/core/res/res/drawable-mdpi/vpn_disconnected.png
Binary files differ
diff --git a/core/res/res/drawable-nodpi/indicator_code_lock_drag_direction_green_up.png b/core/res/res/drawable-nodpi/indicator_code_lock_drag_direction_green_up.png
new file mode 100644
index 0000000..cc46f19
--- /dev/null
+++ b/core/res/res/drawable-nodpi/indicator_code_lock_drag_direction_green_up.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/indicator_code_lock_drag_direction_green_up.png b/core/res/res/drawable-xhdpi/indicator_code_lock_drag_direction_green_up.png
deleted file mode 100644
index a89b8d5..0000000
--- a/core/res/res/drawable-xhdpi/indicator_code_lock_drag_direction_green_up.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/stat_notify_wifi_in_range.png b/core/res/res/drawable-xhdpi/stat_notify_wifi_in_range.png
index 1909183..6bb7512 100644
--- a/core/res/res/drawable-xhdpi/stat_notify_wifi_in_range.png
+++ b/core/res/res/drawable-xhdpi/stat_notify_wifi_in_range.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/vpn_connected.png b/core/res/res/drawable-xhdpi/vpn_connected.png
index 5d37ffc..1f46be2 100644
--- a/core/res/res/drawable-xhdpi/vpn_connected.png
+++ b/core/res/res/drawable-xhdpi/vpn_connected.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/vpn_disconnected.png b/core/res/res/drawable-xhdpi/vpn_disconnected.png
index dd9ba92..847d3f5 100644
--- a/core/res/res/drawable-xhdpi/vpn_disconnected.png
+++ b/core/res/res/drawable-xhdpi/vpn_disconnected.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-hdpi/indicator_code_lock_drag_direction_green_up.png b/core/res/res/drawable-xlarge-hdpi/indicator_code_lock_drag_direction_green_up.png
deleted file mode 100644
index c605607..0000000
--- a/core/res/res/drawable-xlarge-hdpi/indicator_code_lock_drag_direction_green_up.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5eb3e5a..ae39760 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -138,6 +138,18 @@
<item>"0,1"</item>
</string-array>
+ <!-- Set of NetworkInfo.getType() that reflect data usage. -->
+ <integer-array translatable="false" name="config_data_usage_network_types">
+ <item>0</item> <!-- TYPE_MOBILE -->
+ <item>2</item> <!-- TYPE_MOBILE_MMS -->
+ <item>3</item> <!-- TYPE_MOBILE_SUPL -->
+ <item>4</item> <!-- TYPE_MOBILE_DUN -->
+ <item>5</item> <!-- TYPE_MOBILE_HIPRI -->
+ <item>10</item> <!-- TYPE_MOBILE_FOTA -->
+ <item>11</item> <!-- TYPE_MOBILE_IMS -->
+ <item>12</item> <!-- TYPE_MOBILE_CBS -->
+ </integer-array>
+
<!-- The maximum duration (in milliseconds) we expect a network transition to take -->
<integer name="config_networkTransitionTimeout">60000</integer>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e093fa9..2d5d4cc 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -41,9 +41,13 @@
<string name="untitled"><untitled></string>
<!-- Used to replace a range of characters in text that is too wide
- for the space allocated to it. -->
+ for the space allocated to it (three dots). -->
<string name="ellipsis">\u2026</string>
+ <!-- Used to replace a range of characters in text that is too wide
+ for the space allocated to it (two dots). -->
+ <string name="ellipsis_two_dots">\u2025</string>
+
<!-- How to display the lack of a phone number -->
<string name="emptyPhoneNumber">(No phone number)</string>
diff --git a/core/tests/coretests/apks/install_verifier_bad/Android.mk b/core/tests/coretests/apks/install_verifier_bad/Android.mk
new file mode 100644
index 0000000..b50cfd04
--- /dev/null
+++ b/core/tests/coretests/apks/install_verifier_bad/Android.mk
@@ -0,0 +1,11 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := FrameworkCoreTests_install_verifier_bad
+
+include $(BUILD_PACKAGE)
+
diff --git a/core/tests/coretests/apks/install_verifier_bad/AndroidManifest.xml b/core/tests/coretests/apks/install_verifier_bad/AndroidManifest.xml
new file mode 100644
index 0000000..0170cdd
--- /dev/null
+++ b/core/tests/coretests/apks/install_verifier_bad/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.coretests.install_verifier_bad">
+
+ <package-verifier android:name="com.android.frameworks.coretests.nonexistent" android:publicKey="Zm9vYmFy" />
+
+ <application android:hasCode="false">
+ </application>
+</manifest>
diff --git a/core/tests/coretests/apks/install_verifier_bad/res/values/strings.xml b/core/tests/coretests/apks/install_verifier_bad/res/values/strings.xml
new file mode 100644
index 0000000..3b8b3b1
--- /dev/null
+++ b/core/tests/coretests/apks/install_verifier_bad/res/values/strings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Just need this dummy file to have something to build. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="dummy">dummy</string>
+</resources>
diff --git a/core/tests/coretests/apks/install_verifier_good/Android.mk b/core/tests/coretests/apks/install_verifier_good/Android.mk
new file mode 100644
index 0000000..a48a80e
--- /dev/null
+++ b/core/tests/coretests/apks/install_verifier_good/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := FrameworkCoreTests_install_verifier_good
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/install_verifier_good/AndroidManifest.xml b/core/tests/coretests/apks/install_verifier_good/AndroidManifest.xml
new file mode 100644
index 0000000..90135a5
--- /dev/null
+++ b/core/tests/coretests/apks/install_verifier_good/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.coretests.install_verifier_bad">
+
+ <package-verifier android:name="com.android.frameworks.coretests" android:publicKey="MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKCAQEAnHgFkqwNXTgc3qpl7MimAG42SAxtcgexIBG+UIY6q+K1XQCa33FG1vIgIoDHzU172yYkO4qAbCazSxN1I6SSaCJJBNwBST58Cs8aBch09psDe2AwnZB00kKA4WutKoc0NhlR6vcqSC0JsgSxh14SrJjBqnc9aAC56v3lbVi+2OjaFvmjYAmcN6g0pt/tt7a0SgSeB6Jp/M8sVJbyzzbWTfkKO42PNKO6q0z1M3GrJ3GbO6WHVK0MU/wU4dtF1R4jT7vpPJuk7fnOVCYTUOxTVge/aaL/SqB9tffqIA0JpsG0niFAL4ntEZCJOqtakYDxUugvhaRXU89fwZBxxe7IJwIBAw==" />
+
+ <application android:hasCode="false">
+ </application>
+</manifest>
diff --git a/core/tests/coretests/apks/install_verifier_good/res/values/strings.xml b/core/tests/coretests/apks/install_verifier_good/res/values/strings.xml
new file mode 100644
index 0000000..3b8b3b1
--- /dev/null
+++ b/core/tests/coretests/apks/install_verifier_good/res/values/strings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Just need this dummy file to have something to build. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="dummy">dummy</string>
+</resources>
diff --git a/data/fonts/Lohit_Hindi.ttf b/data/fonts/Lohit_Hindi.ttf
deleted file mode 100644
index 73caae3..0000000
--- a/data/fonts/Lohit_Hindi.ttf
+++ /dev/null
Binary files differ
diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml
index e468558..881233a 100644
--- a/data/fonts/fallback_fonts.xml
+++ b/data/fonts/fallback_fonts.xml
@@ -56,7 +56,7 @@
</family>
<family>
<fileset>
- <file>Lohit_Hindi.ttf</file>
+ <file>Lohit-Devanagari.ttf</file>
</fileset>
</family>
<family>
diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk
index 5bac8f0..67d04c9 100644
--- a/data/fonts/fonts.mk
+++ b/data/fonts/fonts.mk
@@ -30,7 +30,6 @@
frameworks/base/data/fonts/DroidSerif-Italic.ttf:system/fonts/DroidSerif-Italic.ttf \
frameworks/base/data/fonts/DroidSerif-BoldItalic.ttf:system/fonts/DroidSerif-BoldItalic.ttf \
frameworks/base/data/fonts/DroidSansMono.ttf:system/fonts/DroidSansMono.ttf \
- frameworks/base/data/fonts/Lohit_Hindi.ttf:system/fonts/Lohit_Hindi.ttf \
frameworks/base/data/fonts/DroidSansArmenian.ttf:system/fonts/DroidSansArmenian.ttf \
frameworks/base/data/fonts/DroidSansGeorgian.ttf:system/fonts/DroidSansGeorgian.ttf \
frameworks/base/data/fonts/Clockopia.ttf:system/fonts/Clockopia.ttf \
diff --git a/data/sounds/effects/ogg/Effect_Tick.ogg b/data/sounds/effects/ogg/Effect_Tick.ogg
index b379019..c8a5c36 100644
--- a/data/sounds/effects/ogg/Effect_Tick.ogg
+++ b/data/sounds/effects/ogg/Effect_Tick.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/KeypressDelete_14.ogg b/data/sounds/effects/ogg/KeypressDelete_14.ogg
index aa4349d..0baea25 100644
--- a/data/sounds/effects/ogg/KeypressDelete_14.ogg
+++ b/data/sounds/effects/ogg/KeypressDelete_14.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/KeypressDelete_49.ogg b/data/sounds/effects/ogg/KeypressDelete_49.ogg
index aa4349d..f5e9deb 100644
--- a/data/sounds/effects/ogg/KeypressDelete_49.ogg
+++ b/data/sounds/effects/ogg/KeypressDelete_49.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/KeypressReturn_14.ogg b/data/sounds/effects/ogg/KeypressReturn_14.ogg
index 2244a42..86a5a0e 100644
--- a/data/sounds/effects/ogg/KeypressReturn_14.ogg
+++ b/data/sounds/effects/ogg/KeypressReturn_14.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/KeypressReturn_49.ogg b/data/sounds/effects/ogg/KeypressReturn_49.ogg
index 2244a42..b0ddee5 100644
--- a/data/sounds/effects/ogg/KeypressReturn_49.ogg
+++ b/data/sounds/effects/ogg/KeypressReturn_49.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/KeypressSpacebar_14.ogg b/data/sounds/effects/ogg/KeypressSpacebar_14.ogg
index 56ea15e..058534a 100644
--- a/data/sounds/effects/ogg/KeypressSpacebar_14.ogg
+++ b/data/sounds/effects/ogg/KeypressSpacebar_14.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/KeypressSpacebar_49.ogg b/data/sounds/effects/ogg/KeypressSpacebar_49.ogg
index 56ea15e..3866e0e9 100644
--- a/data/sounds/effects/ogg/KeypressSpacebar_49.ogg
+++ b/data/sounds/effects/ogg/KeypressSpacebar_49.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/KeypressStandard_14.ogg b/data/sounds/effects/ogg/KeypressStandard_14.ogg
index 8df7214..317c8f3 100644
--- a/data/sounds/effects/ogg/KeypressStandard_14.ogg
+++ b/data/sounds/effects/ogg/KeypressStandard_14.ogg
Binary files differ
diff --git a/data/sounds/effects/ogg/KeypressStandard_49.ogg b/data/sounds/effects/ogg/KeypressStandard_49.ogg
index 8df7214..893bb52 100644
--- a/data/sounds/effects/ogg/KeypressStandard_49.ogg
+++ b/data/sounds/effects/ogg/KeypressStandard_49.ogg
Binary files differ
diff --git a/docs/html/guide/appendix/media-formats.jd b/docs/html/guide/appendix/media-formats.jd
index e128a1c..ccc63a2 100644
--- a/docs/html/guide/appendix/media-formats.jd
+++ b/docs/html/guide/appendix/media-formats.jd
@@ -37,11 +37,16 @@
<ul>
<li>RTSP (RTP, SDP)</li>
- <li>HTTP progressive streaming</li>
- <li>HTTP live streaming <a href="http://tools.ietf.org/html/draft-pantos-http-live-streaming-05">draft protocol</a> (Android 3.0 and above)</li>
+ <li>HTTP/HTTPS progressive streaming</li>
+ <li>HTTP/HTTPS live streaming <a href="http://tools.ietf.org/html/draft-pantos-http-live-streaming">draft protocol</a>: <ul>
+ <li>MPEG-2 TS media files only</li>
+ <li>Protocol version 3 (Android 4.0 and above)</li>
+ <li>Protocol version 2 (Android 3.x)</li>
+ <li>Not supported before Android 3.0</li>
+ </ul></li>
</ul>
-<p class="note"><strong>Note:</strong> HTTPS is not supported at this time.</p>
+<p class="note"><strong>Note:</strong> HTTPS is not supported before Android 3.1.</p>
<h2 id="core">Core Media Formats</h2>
@@ -71,7 +76,11 @@
<td style="text-align: center;"><big>•</big></td>
<td rowspan="3">Mono/Stereo content in any combination of standard bit
rates up to 160 kbps and sampling rates from 8 to 48kHz</td>
-<td rowspan="3">3GPP (.3gp), and MPEG-4 (.mp4, .m4a). ADTS raw AAC (.aac, decode only, ADIF not supported, Android 3.1+). </td>
+<td rowspan="3">
+ • 3GPP (.3gp)<br>
+ • MPEG-4 (.mp4, .m4a)<br>
+ • ADTS raw AAC (.aac, decode in Android 3.1+, encode in Android 4.0+, ADIF not supported)<br>
+ • MPEG-TS (.ts, not seekable, Android 3.0+)</td>
</tr>
<tr>
@@ -91,8 +100,8 @@
<td style="text-align: center;"><big>•</big></td>
<td style="text-align: center;"><big>•</big></td>
<td>4.75 to 12.2 kbps sampled @ 8kHz</td>
-<td>3GPP (.3gp)
-</td>
+<td>
+ 3GPP (.3gp)</td>
</tr>
<tr>
@@ -100,19 +109,21 @@
<td style="text-align: center;"><big>•</big></td>
<td style="text-align: center;"><big>•</big></td>
<td>9 rates from 6.60 kbit/s to 23.85 kbit/s sampled @ 16kHz</td>
-<td>3GPP (.3gp)</td>
+<td>
+ 3GPP (.3gp)</td>
</tr>
<tr>
<td>FLAC</td>
<td> </td>
-<td style="text-align: center;"><big>•</big><br><small>(Android 3.1+)</small></td>
+<td style="text-align: center;" nowrap><big>•</big><br><small>(Android 3.1+)</small></td>
<td>Mono/Stereo (no multichannel). Sample rates up to 48 kHz (but up to 44.1
kHz is recommended on devices with 44.1 kHz output, as the 48 to 44.1 kHz
downsampler does not include a low-pass filter). 16-bit recommended;
no dither applied for 24-bit.
</td>
-<td>FLAC (.flac) only</td>
+<td>
+ FLAC (.flac) only</td>
</tr>
<tr>
@@ -121,7 +132,8 @@
<td style="text-align: center;"><big>•</big></td>
<td>Mono/Stereo 8-320Kbps constant (CBR) or variable bit-rate (VBR)
</td>
-<td>MP3 (.mp3)</td>
+<td>
+ MP3 (.mp3)</td>
</tr>
<tr>
@@ -129,15 +141,21 @@
<td> </td>
<td style="text-align: center;"><big>•</big></td>
<td>MIDI Type 0 and 1. DLS Version 1 and 2. XMF and Mobile XMF. Support for ringtone formats RTTTL/RTX, OTA, and iMelody </td>
-<td>Type 0 and 1 (.mid, .xmf, .mxmf). Also RTTTL/RTX (.rtttl, .rtx), OTA (.ota), and iMelody (.imy)</td>
+<td>
+ • Type 0 and 1 (.mid, .xmf, .mxmf)<br>
+ • RTTTL/RTX (.rtttl, .rtx)<br>
+ • OTA (.ota)<br>
+ • iMelody (.imy)</td>
</tr>
<tr>
-<td>Ogg Vorbis</td>
+<td>Vorbis</td>
<td> </td>
<td style="text-align: center;"><big>•</big></td>
<td> </td>
-<td>Ogg (.ogg)</td>
+<td>
+ • Ogg (.ogg)<br>
+ • Matroska (.mkv, Android 4.0+)</td>
</tr>
<tr>
@@ -145,16 +163,18 @@
<td> </td>
<td style="text-align: center;"><big>•</big></td>
<td>8- and 16-bit linear PCM (rates up to limit of hardware)</td>
-<td>WAVE (.wav)</td>
+<td>
+ WAVE (.wav)</td>
</tr>
<tr>
-<td rowspan="4">Image</td>
+<td rowspan="5">Image</td>
<td>JPEG</td>
<td style="text-align: center;"><big>•</big></td>
<td style="text-align: center;"><big>•</big></td>
<td>Base+progressive</td>
-<td>JPEG (.jpg)</td>
+<td>
+ JPEG (.jpg)</td>
</tr>
<tr>
@@ -162,7 +182,8 @@
<td> </td>
<td style="text-align: center;"><big>•</big></td>
<td> </td>
-<td>GIF (.gif)</td>
+<td>
+ GIF (.gif)</td>
</tr>
<tr>
@@ -170,7 +191,8 @@
<td style="text-align: center;"><big>•</big></td>
<td style="text-align: center;"><big>•</big></td>
<td> </td>
-<td>PNG (.png)</td>
+<td>
+ PNG (.png)</td>
</tr>
<tr>
@@ -178,7 +200,17 @@
<td> </td>
<td style="text-align: center;"><big>•</big></td>
<td> </td>
-<td>BMP (.bmp)</td>
+<td>
+ BMP (.bmp)</td>
+</tr>
+
+<tr>
+<td>WEBP</td>
+<td style="text-align: center;" nowrap><big>•</big><br><small>(Android 4.0+)</small></td>
+<td style="text-align: center;" nowrap><big>•</big><br><small>(Android 4.0+)</small></td>
+<td> </td>
+<td>
+ WebP (.webp)</td>
</tr>
@@ -188,15 +220,20 @@
<td style="text-align: center;"><big>•</big></td>
<td style="text-align: center;"><big>•</big></td>
<td> </td>
-<td>3GPP (.3gp) and MPEG-4 (.mp4)</td>
+<td>
+ • 3GPP (.3gp)<br>
+ • MPEG-4 (.mp4)</td>
</tr>
<tr>
<td>H.264 AVC</td>
<td style="text-align: center;" nowrap><big>•</big><br><small>(Android 3.0+)</small></td>
-<td style="text-align: center;"><big>•</big></td>
+<td style="text-align: center;" nowrap><big>•</big></td>
<td>Baseline Profile (BP)</td>
-<td>3GPP (.3gp) and MPEG-4 (.mp4). MPEG-TS (.ts, AAC audio only, not seekable, Android 3.0+)</td>
+<td>
+ • 3GPP (.3gp)<br>
+ • MPEG-4 (.mp4)<br>
+ • MPEG-TS (.ts, AAC audio only, not seekable, Android 3.0+)</td>
</tr>
<tr>
@@ -204,15 +241,18 @@
<td> </td>
<td style="text-align: center;"><big>•</big></td>
<td> </td>
-<td>3GPP (.3gp)</td>
+<td>
+ 3GPP (.3gp)</td>
</tr>
<tr>
<td>VP8</td>
<td> </td>
-<td style="text-align: center;"><big>•</big><br><small>(Android 2.3.3+)</small></td>
-<td> </td>
-<td><a href="http://www.webmproject.org/">WebM</a> (.webm)</td>
+<td style="text-align: center;" nowrap><big>•</big><br><small>(Android 2.3.3+)</small></td>
+<td>Streamable only in Android 4.0 and above</td>
+<td>
+ • <a href="http://www.webmproject.org/">WebM</a> (.webm)<br>
+ • Matroska (.mkv, Android 4.0+)</td>
</tr>
</tbody></table>
@@ -220,7 +260,7 @@
<h2 id="recommendations">Video Encoding Recommendations</h2>
-<p>Table 2, below, lists examples of video encoding profiles and parameters that the Android media framework supports for playback. In addition to these encoding parameter recommendations, a device's available video recording profiles can be used as a proxy for media playback capabilities. These profiles can be inspected using the {@link android.media.CamcorderProfile CamcorderProfile} class, which is available since API level 8.</p>
+<p>Table 2, below, lists examples of video encoding profiles and parameters that the Android media framework supports for playback. In addition to these encoding parameter recommendations, a device's available <em>video recording</em> profiles can be used as a proxy for media playback capabilities. These profiles can be inspected using the {@link android.media.CamcorderProfile CamcorderProfile} class, which is available since API level 8.</p>
<p class="table-caption" id="encoding-recommendations-table"><strong>Table 2.</strong> Examples of supported video encoding parameters.</p>
@@ -228,45 +268,53 @@
<thead>
<tr>
<th> </th>
- <th style="background-color:#f3f3f3;font-weight:normal">Lower quality</th>
- <th style="background-color:#f3f3f3;font-weight:normal">Higher quality</th>
+ <th style="background-color:#f3f3f3;font-weight:normal"><acronym title="Standard definition">SD</a> (Low quality)</th>
+ <th style="background-color:#f3f3f3;font-weight:normal"><acronym title="Standard definition">SD</a> (High quality)</th>
+ <th style="background-color:#f3f3f3;font-weight:normal"><acronym title="High definition">HD</a> (Not available on all devices)</th>
</tr>
</thead>
<tbody>
<tr>
<th>Video codec</th>
- <td>H.264 Baseline Profile</th>
- <td>H.264 Baseline Profile</th>
+ <td>H.264 Baseline Profile</td>
+ <td>H.264 Baseline Profile</td>
+ <td>H.264 Baseline Profile</td>
</tr>
<tr>
<th>Video resolution</th>
- <td>176 x 144 px</th>
- <td>480 x 360 px</th>
+ <td>176 x 144 px</td>
+ <td>480 x 360 px</td>
+ <td>1280 x 720 px</td>
</tr>
<tr>
<th>Video frame rate</th>
- <td>12 fps</th>
- <td>30 fps</th>
+ <td>12 fps</td>
+ <td>30 fps</td>
+ <td>30 fps</td>
</tr>
<tr>
<th>Video bitrate</th>
- <td>56 Kbps</th>
- <td>500 Kbps</th>
+ <td>56 Kbps</td>
+ <td>500 Kbps</td>
+ <td>2 Mbps</td>
</tr>
<tr>
<th>Audio codec</th>
- <td>AAC-LC</th>
- <td>AAC-LC</th>
+ <td>AAC-LC</td>
+ <td>AAC-LC</td>
+ <td>AAC-LC</td>
</tr>
<tr>
<th>Audio channels</th>
- <td>1 (mono)</th>
- <td>2 (stereo)</th>
+ <td>1 (mono)</td>
+ <td>2 (stereo)</td>
+ <td>2 (stereo)</td>
</tr>
<tr>
<th>Audio bitrate</th>
- <td>24 Kbps</th>
- <td>128 Kbps</th>
+ <td>24 Kbps</td>
+ <td>128 Kbps</td>
+ <td>192 Kbps</td>
</tr>
</tbody>
</table>
@@ -274,7 +322,8 @@
<p style="margin-top: 2em">For video content that is streamed over HTTP or RTSP, there are additional requirements:</p>
<ul>
- <li>For 3GPP and MPEG-4 containers, the <code>moov</code> atom must precede any <code>mdat</code> atoms.</li>
+ <li>For 3GPP and MPEG-4 containers, the <code>moov</code> atom must precede any <code>mdat</code> atoms, but must succeed the
+ <code>ftyp</code> atom.</li>
<li>For 3GPP, MPEG-4, and WebM containers, audio and video samples corresponding to the same time offset may be no more than 500 KB apart.
To minimize this audio/video drift, consider interleaving audio and video in smaller chunk sizes.</li>
</ul>
diff --git a/docs/html/resources/resources-data.js b/docs/html/resources/resources-data.js
index f799b0c..6c5d882 100644
--- a/docs/html/resources/resources-data.js
+++ b/docs/html/resources/resources-data.js
@@ -323,6 +323,16 @@
}
},
{
+ tags: ['article', 'accountsync', 'data'],
+ path: 'articles/contacts.html',
+ title: {
+ en: 'Using the Contacts API'
+ },
+ description: {
+ en: 'Android provides a Contacts API for managing and integrating contacts from multiple accounts and data sources and allows apps to read various information about individual contacts.'
+ }
+ },
+ {
tags: ['article', 'ui', 'web'],
path: 'articles/using-webviews.html',
title: {
diff --git a/graphics/java/android/renderscript/ProgramRaster.java b/graphics/java/android/renderscript/ProgramRaster.java
index bc0d928..60d9698 100644
--- a/graphics/java/android/renderscript/ProgramRaster.java
+++ b/graphics/java/android/renderscript/ProgramRaster.java
@@ -84,14 +84,10 @@
public static class Builder {
RenderScript mRS;
boolean mPointSprite;
- boolean mPointSmooth;
- boolean mLineSmooth;
CullMode mCullMode;
public Builder(RenderScript rs) {
mRS = rs;
- mPointSmooth = false;
- mLineSmooth = false;
mPointSprite = false;
mCullMode = CullMode.BACK;
}
@@ -108,8 +104,7 @@
public ProgramRaster create() {
mRS.validate();
- int id = mRS.nProgramRasterCreate(mPointSmooth, mLineSmooth, mPointSprite,
- 1.f, mCullMode.mID);
+ int id = mRS.nProgramRasterCreate(mPointSprite, mCullMode.mID);
return new ProgramRaster(id, mRS);
}
}
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index 571b895..e2950d5e 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -522,13 +522,10 @@
dstMode, depthFunc);
}
- native int rsnProgramRasterCreate(int con, boolean pointSmooth, boolean lineSmooth,
- boolean pointSprite, float lineWidth, int cullMode);
- synchronized int nProgramRasterCreate(boolean pointSmooth, boolean lineSmooth,
- boolean pointSprite, float lineWidth, int cullMode) {
+ native int rsnProgramRasterCreate(int con, boolean pointSprite, int cullMode);
+ synchronized int nProgramRasterCreate(boolean pointSprite, int cullMode) {
validate();
- return rsnProgramRasterCreate(mContext, pointSmooth, lineSmooth, pointSprite, lineWidth,
- cullMode);
+ return rsnProgramRasterCreate(mContext, pointSprite, cullMode);
}
native void rsnProgramBindConstants(int con, int pv, int slot, int mID);
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index d7ac5d8..ec1f8de 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -1053,12 +1053,10 @@
// ---------------------------------------------------------------------------
static jint
-nProgramRasterCreate(JNIEnv *_env, jobject _this, RsContext con, jboolean pointSmooth,
- jboolean lineSmooth, jboolean pointSprite, jfloat lineWidth, jint cull)
+nProgramRasterCreate(JNIEnv *_env, jobject _this, RsContext con, jboolean pointSprite, jint cull)
{
- LOG_API("nProgramRasterCreate, con(%p), pointSmooth(%i), lineSmooth(%i), pointSprite(%i)",
- con, pointSmooth, lineSmooth, pointSprite);
- return (jint)rsProgramRasterCreate(con, pointSmooth, lineSmooth, pointSprite, lineWidth, (RsCullMode)cull);
+ LOG_API("nProgramRasterCreate, con(%p), pointSprite(%i), cull(%i)", con, pointSprite, cull);
+ return (jint)rsProgramRasterCreate(con, pointSprite, (RsCullMode)cull);
}
@@ -1295,7 +1293,7 @@
{"rsnProgramBindSampler", "(IIII)V", (void*)nProgramBindSampler },
{"rsnProgramFragmentCreate", "(ILjava/lang/String;[I)I", (void*)nProgramFragmentCreate },
-{"rsnProgramRasterCreate", "(IZZZFI)I", (void*)nProgramRasterCreate },
+{"rsnProgramRasterCreate", "(IZI)I", (void*)nProgramRasterCreate },
{"rsnProgramVertexCreate", "(ILjava/lang/String;[I)I", (void*)nProgramVertexCreate },
{"rsnContextBindRootScript", "(II)V", (void*)nContextBindRootScript },
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index b7286e5..9da9907 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -113,6 +113,7 @@
Vector<BufferInfo> mBuffers[2];
bool mPortEOS[2];
+ status_t mInputEOSResult;
List<sp<AMessage> > mDeferredQueue;
diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec
index f277582..20b1f52 100644
--- a/libs/rs/rs.spec
+++ b/libs/rs/rs.spec
@@ -329,10 +329,7 @@
ProgramRasterCreate {
direct
- param bool pointSmooth
- param bool lineSmooth
param bool pointSprite
- param float lineWidth
param RsCullMode cull
ret RsProgramRaster
}
diff --git a/libs/rs/rsProgramRaster.cpp b/libs/rs/rsProgramRaster.cpp
index 945b5ec..94bfe42 100644
--- a/libs/rs/rsProgramRaster.cpp
+++ b/libs/rs/rsProgramRaster.cpp
@@ -21,19 +21,12 @@
using namespace android::renderscript;
-ProgramRaster::ProgramRaster(Context *rsc, bool pointSmooth,
- bool lineSmooth, bool pointSprite,
- float lineWidth, RsCullMode cull)
+ProgramRaster::ProgramRaster(Context *rsc, bool pointSprite, RsCullMode cull)
: ProgramBase(rsc) {
memset(&mHal, 0, sizeof(mHal));
-
- mHal.state.pointSmooth = pointSmooth;
- mHal.state.lineSmooth = lineSmooth;
mHal.state.pointSprite = pointSprite;
- mHal.state.lineWidth = lineWidth;
mHal.state.cull = cull;
-
rsc->mHal.funcs.raster.init(rsc, this);
}
@@ -74,8 +67,7 @@
}
void ProgramRasterState::init(Context *rsc) {
- mDefault.set(ProgramRaster::getProgramRaster(rsc, false, false,
- false, 1.f, RS_CULL_BACK).get());
+ mDefault.set(ProgramRaster::getProgramRaster(rsc, false, RS_CULL_BACK).get());
}
void ProgramRasterState::deinit(Context *rsc) {
@@ -84,19 +76,13 @@
}
ObjectBaseRef<ProgramRaster> ProgramRaster::getProgramRaster(Context *rsc,
- bool pointSmooth,
- bool lineSmooth,
bool pointSprite,
- float lineWidth,
RsCullMode cull) {
ObjectBaseRef<ProgramRaster> returnRef;
ObjectBase::asyncLock();
for (uint32_t ct = 0; ct < rsc->mStateRaster.mRasterPrograms.size(); ct++) {
ProgramRaster *existing = rsc->mStateRaster.mRasterPrograms[ct];
- if (existing->mHal.state.pointSmooth != pointSmooth) continue;
- if (existing->mHal.state.lineSmooth != lineSmooth) continue;
if (existing->mHal.state.pointSprite != pointSprite) continue;
- if (existing->mHal.state.lineWidth != lineWidth) continue;
if (existing->mHal.state.cull != cull) continue;
returnRef.set(existing);
ObjectBase::asyncUnlock();
@@ -104,8 +90,7 @@
}
ObjectBase::asyncUnlock();
- ProgramRaster *pr = new ProgramRaster(rsc, pointSmooth,
- lineSmooth, pointSprite, lineWidth, cull);
+ ProgramRaster *pr = new ProgramRaster(rsc, pointSprite, cull);
returnRef.set(pr);
ObjectBase::asyncLock();
@@ -118,10 +103,8 @@
namespace android {
namespace renderscript {
-RsProgramRaster rsi_ProgramRasterCreate(Context * rsc, bool pointSmooth, bool lineSmooth,
- bool pointSprite, float lineWidth, RsCullMode cull) {
- ObjectBaseRef<ProgramRaster> pr = ProgramRaster::getProgramRaster(rsc, pointSmooth, lineSmooth,
- pointSprite, lineWidth, cull);
+RsProgramRaster rsi_ProgramRasterCreate(Context * rsc, bool pointSprite, RsCullMode cull) {
+ ObjectBaseRef<ProgramRaster> pr = ProgramRaster::getProgramRaster(rsc, pointSprite, cull);
pr->incUserRef();
return pr.get();
}
diff --git a/libs/rs/rsProgramRaster.h b/libs/rs/rsProgramRaster.h
index 09d7d54..20af30a 100644
--- a/libs/rs/rsProgramRaster.h
+++ b/libs/rs/rsProgramRaster.h
@@ -33,19 +33,13 @@
static ProgramRaster *createFromStream(Context *rsc, IStream *stream);
static ObjectBaseRef<ProgramRaster> getProgramRaster(Context *rsc,
- bool pointSmooth,
- bool lineSmooth,
bool pointSprite,
- float lineWidth,
RsCullMode cull);
struct Hal {
mutable void *drv;
struct State {
- bool pointSmooth;
- bool lineSmooth;
bool pointSprite;
- float lineWidth;
RsCullMode cull;
};
State state;
@@ -58,10 +52,7 @@
private:
ProgramRaster(Context *rsc,
- bool pointSmooth,
- bool lineSmooth,
bool pointSprite,
- float lineWidth,
RsCullMode cull);
};
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index 8dab291..5656088 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -482,6 +482,7 @@
if (!rhs.isValid()) {
LOGE("Region::boolean_operation(op=%d) invalid Rect={%d,%d,%d,%d}",
op, rhs.left, rhs.top, rhs.right, rhs.bottom);
+ return;
}
#if VALIDATE_WITH_CORECG || VALIDATE_REGIONS
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
index 605d056..fb14204 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
@@ -110,7 +110,11 @@
if (n == -EWOULDBLOCK) {
break;
} else if (n < 0) {
- LOGI("input data EOS reached.");
+ if (n != ERROR_END_OF_STREAM) {
+ LOGI("input data EOS reached, error %d", n);
+ } else {
+ LOGI("input data EOS reached.");
+ }
mTSParser->signalEOS(n);
mEOS = true;
break;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 7218faf..7bfd358 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -272,7 +272,18 @@
}
}
} else if (what == ACodec::kWhatEOS) {
- mRenderer->queueEOS(audio, ERROR_END_OF_STREAM);
+ int32_t err;
+ CHECK(codecRequest->findInt32("err", &err));
+
+ if (err == ERROR_END_OF_STREAM) {
+ LOGV("got %s decoder EOS", audio ? "audio" : "video");
+ } else {
+ LOGV("got %s decoder EOS w/ error %d",
+ audio ? "audio" : "video",
+ err);
+ }
+
+ mRenderer->queueEOS(audio, err);
} else if (what == ACodec::kWhatFlushCompleted) {
bool needShutdown;
@@ -397,7 +408,7 @@
if (finalResult == ERROR_END_OF_STREAM) {
LOGV("reached %s EOS", audio ? "audio" : "video");
} else {
- LOGE("%s track encountered an error (0x%08x)",
+ LOGE("%s track encountered an error (%d)",
audio ? "audio" : "video", finalResult);
notifyListener(
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index a2d9e59..a3746cd 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -323,6 +323,7 @@
mFlushingState = new FlushingState(this);
mPortEOS[kPortIndexInput] = mPortEOS[kPortIndexOutput] = false;
+ mInputEOSResult = OK;
changeState(mUninitializedState);
}
@@ -1347,7 +1348,10 @@
case KEEP_BUFFERS:
{
if (buffer == NULL) {
- mCodec->mPortEOS[kPortIndexInput] = true;
+ if (!mCodec->mPortEOS[kPortIndexInput]) {
+ mCodec->mPortEOS[kPortIndexInput] = true;
+ mCodec->mInputEOSResult = err;
+ }
}
break;
}
@@ -1398,8 +1402,14 @@
getMoreInputDataIfPossible();
} else if (!mCodec->mPortEOS[kPortIndexInput]) {
- LOGV("[%s] Signalling EOS on the input port",
- mCodec->mComponentName.c_str());
+ if (err != ERROR_END_OF_STREAM) {
+ LOGV("[%s] Signalling EOS on the input port "
+ "due to error %d",
+ mCodec->mComponentName.c_str(), err);
+ } else {
+ LOGV("[%s] Signalling EOS on the input port",
+ mCodec->mComponentName.c_str());
+ }
LOGV("[%s] calling emptyBuffer %p signalling EOS",
mCodec->mComponentName.c_str(), bufferID);
@@ -1416,6 +1426,7 @@
info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
mCodec->mPortEOS[kPortIndexInput] = true;
+ mCodec->mInputEOSResult = err;
}
break;
@@ -1523,6 +1534,7 @@
if (flags & OMX_BUFFERFLAG_EOS) {
sp<AMessage> notify = mCodec->mNotify->dup();
notify->setInt32("what", ACodec::kWhatEOS);
+ notify->setInt32("err", mCodec->mInputEOSResult);
notify->post();
mCodec->mPortEOS[kPortIndexOutput] = true;
@@ -1721,6 +1733,8 @@
mCodec->mPortEOS[kPortIndexInput] =
mCodec->mPortEOS[kPortIndexOutput] = false;
+ mCodec->mInputEOSResult = OK;
+
mCodec->configureCodec(mime.c_str(), msg);
sp<RefBase> obj;
@@ -2371,6 +2385,8 @@
mCodec->mPortEOS[kPortIndexInput] =
mCodec->mPortEOS[kPortIndexOutput] = false;
+ mCodec->mInputEOSResult = OK;
+
mCodec->changeState(mCodec->mExecutingState);
}
}
diff --git a/media/libstagefright/AVIExtractor.cpp b/media/libstagefright/AVIExtractor.cpp
index d47e5d1..0be2ca4 100644
--- a/media/libstagefright/AVIExtractor.cpp
+++ b/media/libstagefright/AVIExtractor.cpp
@@ -56,11 +56,36 @@
MediaBufferGroup *mBufferGroup;
size_t mSampleIndex;
+ sp<MP3Splitter> mSplitter;
+
DISALLOW_EVIL_CONSTRUCTORS(AVISource);
};
////////////////////////////////////////////////////////////////////////////////
+struct AVIExtractor::MP3Splitter : public RefBase {
+ MP3Splitter();
+
+ void clear();
+ void append(MediaBuffer *buffer);
+ status_t read(MediaBuffer **buffer);
+
+protected:
+ virtual ~MP3Splitter();
+
+private:
+ bool mFindSync;
+ int64_t mBaseTimeUs;
+ int64_t mNumSamplesRead;
+ sp<ABuffer> mBuffer;
+
+ bool resync();
+
+ DISALLOW_EVIL_CONSTRUCTORS(MP3Splitter);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
AVIExtractor::AVISource::AVISource(
const sp<AVIExtractor> &extractor, size_t trackIndex)
: mExtractor(extractor),
@@ -84,6 +109,15 @@
mBufferGroup->add_buffer(new MediaBuffer(mTrack.mMaxSampleSize));
mSampleIndex = 0;
+ const char *mime;
+ CHECK(mTrack.mMeta->findCString(kKeyMIMEType, &mime));
+
+ if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {
+ mSplitter = new MP3Splitter;
+ } else {
+ mSplitter.clear();
+ }
+
return OK;
}
@@ -93,6 +127,8 @@
delete mBufferGroup;
mBufferGroup = NULL;
+ mSplitter.clear();
+
return OK;
}
@@ -116,39 +152,213 @@
if (err != OK) {
return ERROR_END_OF_STREAM;
}
+
+ if (mSplitter != NULL) {
+ mSplitter->clear();
+ }
}
- off64_t offset;
- size_t size;
- bool isKey;
- int64_t timeUs;
- status_t err = mExtractor->getSampleInfo(
- mTrackIndex, mSampleIndex, &offset, &size, &isKey, &timeUs);
+ for (;;) {
+ if (mSplitter != NULL) {
+ status_t err = mSplitter->read(buffer);
- ++mSampleIndex;
+ if (err == OK) {
+ break;
+ } else if (err != -EAGAIN) {
+ return err;
+ }
+ }
- if (err != OK) {
- return ERROR_END_OF_STREAM;
+ off64_t offset;
+ size_t size;
+ bool isKey;
+ int64_t timeUs;
+ status_t err = mExtractor->getSampleInfo(
+ mTrackIndex, mSampleIndex, &offset, &size, &isKey, &timeUs);
+
+ ++mSampleIndex;
+
+ if (err != OK) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ MediaBuffer *out;
+ CHECK_EQ(mBufferGroup->acquire_buffer(&out), (status_t)OK);
+
+ ssize_t n = mExtractor->mDataSource->readAt(offset, out->data(), size);
+
+ if (n < (ssize_t)size) {
+ return n < 0 ? (status_t)n : (status_t)ERROR_MALFORMED;
+ }
+
+ out->set_range(0, size);
+
+ out->meta_data()->setInt64(kKeyTime, timeUs);
+
+ if (isKey) {
+ out->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+ }
+
+ if (mSplitter == NULL) {
+ *buffer = out;
+ break;
+ }
+
+ mSplitter->append(out);
+ out->release();
+ out = NULL;
}
- MediaBuffer *out;
- CHECK_EQ(mBufferGroup->acquire_buffer(&out), (status_t)OK);
+ return OK;
+}
- ssize_t n = mExtractor->mDataSource->readAt(offset, out->data(), size);
+////////////////////////////////////////////////////////////////////////////////
- if (n < (ssize_t)size) {
- return n < 0 ? (status_t)n : (status_t)ERROR_MALFORMED;
+AVIExtractor::MP3Splitter::MP3Splitter()
+ : mFindSync(true),
+ mBaseTimeUs(-1ll),
+ mNumSamplesRead(0) {
+}
+
+AVIExtractor::MP3Splitter::~MP3Splitter() {
+}
+
+void AVIExtractor::MP3Splitter::clear() {
+ mFindSync = true;
+ mBaseTimeUs = -1ll;
+ mNumSamplesRead = 0;
+
+ if (mBuffer != NULL) {
+ mBuffer->setRange(0, 0);
+ }
+}
+
+void AVIExtractor::MP3Splitter::append(MediaBuffer *buffer) {
+ size_t prevCapacity = (mBuffer != NULL) ? mBuffer->capacity() : 0;
+
+ if (mBaseTimeUs < 0) {
+ CHECK(mBuffer == NULL || mBuffer->size() == 0);
+ CHECK(buffer->meta_data()->findInt64(kKeyTime, &mBaseTimeUs));
+ mNumSamplesRead = 0;
}
- out->set_range(0, size);
-
- out->meta_data()->setInt64(kKeyTime, timeUs);
-
- if (isKey) {
- out->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+ if (mBuffer != NULL && mBuffer->offset() > 0) {
+ memmove(mBuffer->base(), mBuffer->data(), mBuffer->size());
+ mBuffer->setRange(0, mBuffer->size());
}
- *buffer = out;
+ if (mBuffer == NULL
+ || mBuffer->size() + buffer->range_length() > prevCapacity) {
+ size_t newCapacity =
+ (prevCapacity + buffer->range_length() + 1023) & ~1023;
+
+ sp<ABuffer> newBuffer = new ABuffer(newCapacity);
+ if (mBuffer != NULL) {
+ memcpy(newBuffer->data(), mBuffer->data(), mBuffer->size());
+ newBuffer->setRange(0, mBuffer->size());
+ } else {
+ newBuffer->setRange(0, 0);
+ }
+ mBuffer = newBuffer;
+ }
+
+ memcpy(mBuffer->data() + mBuffer->size(),
+ (const uint8_t *)buffer->data() + buffer->range_offset(),
+ buffer->range_length());
+
+ mBuffer->setRange(0, mBuffer->size() + buffer->range_length());
+}
+
+bool AVIExtractor::MP3Splitter::resync() {
+ if (mBuffer == NULL) {
+ return false;
+ }
+
+ bool foundSync = false;
+ for (size_t offset = 0; offset + 3 < mBuffer->size(); ++offset) {
+ uint32_t firstHeader = U32_AT(mBuffer->data() + offset);
+
+ size_t frameSize;
+ if (!GetMPEGAudioFrameSize(firstHeader, &frameSize)) {
+ continue;
+ }
+
+ size_t subsequentOffset = offset + frameSize;
+ size_t i = 3;
+ while (i > 0) {
+ if (subsequentOffset + 3 >= mBuffer->size()) {
+ break;
+ }
+
+ static const uint32_t kMask = 0xfffe0c00;
+
+ uint32_t header = U32_AT(mBuffer->data() + subsequentOffset);
+ if ((header & kMask) != (firstHeader & kMask)) {
+ break;
+ }
+
+ if (!GetMPEGAudioFrameSize(header, &frameSize)) {
+ break;
+ }
+
+ subsequentOffset += frameSize;
+ --i;
+ }
+
+ if (i == 0) {
+ foundSync = true;
+ memmove(mBuffer->data(),
+ mBuffer->data() + offset,
+ mBuffer->size() - offset);
+
+ mBuffer->setRange(0, mBuffer->size() - offset);
+ break;
+ }
+ }
+
+ return foundSync;
+}
+
+status_t AVIExtractor::MP3Splitter::read(MediaBuffer **out) {
+ *out = NULL;
+
+ if (mFindSync) {
+ if (!resync()) {
+ return -EAGAIN;
+ }
+
+ mFindSync = false;
+ }
+
+ if (mBuffer->size() < 4) {
+ return -EAGAIN;
+ }
+
+ uint32_t header = U32_AT(mBuffer->data());
+ size_t frameSize;
+ int sampleRate;
+ int numSamples;
+ if (!GetMPEGAudioFrameSize(
+ header, &frameSize, &sampleRate, NULL, NULL, &numSamples)) {
+ return ERROR_MALFORMED;
+ }
+
+ if (mBuffer->size() < frameSize) {
+ return -EAGAIN;
+ }
+
+ MediaBuffer *mbuf = new MediaBuffer(frameSize);
+ memcpy(mbuf->data(), mBuffer->data(), frameSize);
+
+ int64_t timeUs = mBaseTimeUs + (mNumSamplesRead * 1000000ll) / sampleRate;
+ mNumSamplesRead += numSamples;
+
+ mbuf->meta_data()->setInt64(kKeyTime, timeUs);
+
+ mBuffer->setRange(
+ mBuffer->offset() + frameSize, mBuffer->size() - frameSize);
+
+ *out = mbuf;
return OK;
}
@@ -449,6 +659,8 @@
track->mThumbnailSampleSize = 0;
track->mThumbnailSampleIndex = -1;
track->mMaxSampleSize = 0;
+ track->mAvgChunkSize = 1.0;
+ track->mFirstChunkSize = 0;
return OK;
}
@@ -467,8 +679,8 @@
bool isVideo = (track->mKind == Track::VIDEO);
- if ((isVideo && size < 40) || (!isVideo && size < 18)) {
- // Expected a BITMAPINFO or WAVEFORMATEX structure, respectively.
+ if ((isVideo && size < 40) || (!isVideo && size < 16)) {
+ // Expected a BITMAPINFO or WAVEFORMAT(EX) structure, respectively.
return ERROR_MALFORMED;
}
@@ -651,6 +863,47 @@
for (size_t i = 0; i < mTracks.size(); ++i) {
Track *track = &mTracks.editItemAt(i);
+ if (track->mBytesPerSample > 0) {
+ // Assume all chunks are roughly the same size for now.
+
+ // Compute the avg. size of the first 128 chunks (if there are
+ // that many), but exclude the size of the first one, since
+ // it may be an outlier.
+ size_t numSamplesToAverage = track->mSamples.size();
+ if (numSamplesToAverage > 256) {
+ numSamplesToAverage = 256;
+ }
+
+ double avgChunkSize = 0;
+ size_t j;
+ for (j = 0; j <= numSamplesToAverage; ++j) {
+ off64_t offset;
+ size_t size;
+ bool isKey;
+ int64_t dummy;
+
+ status_t err =
+ getSampleInfo(
+ i, j,
+ &offset, &size, &isKey, &dummy);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (j == 0) {
+ track->mFirstChunkSize = size;
+ continue;
+ }
+
+ avgChunkSize += size;
+ }
+
+ avgChunkSize /= numSamplesToAverage;
+
+ track->mAvgChunkSize = avgChunkSize;
+ }
+
int64_t durationUs;
CHECK_EQ((status_t)OK,
getSampleTime(i, track->mSamples.size() - 1, &durationUs));
@@ -687,21 +940,6 @@
return err;
}
}
-
- if (track->mBytesPerSample != 0) {
- // Assume all chunks are the same size for now.
-
- off64_t offset;
- size_t size;
- bool isKey;
- int64_t sampleTimeUs;
- CHECK_EQ((status_t)OK,
- getSampleInfo(
- i, 0,
- &offset, &size, &isKey, &sampleTimeUs));
-
- track->mRate *= size / track->mBytesPerSample;
- }
}
mFoundIndex = true;
@@ -904,6 +1142,18 @@
*isKey = info.mIsKey;
+ if (track.mBytesPerSample > 0) {
+ size_t sampleStartInBytes;
+ if (sampleIndex == 0) {
+ sampleStartInBytes = 0;
+ } else {
+ sampleStartInBytes =
+ track.mFirstChunkSize + track.mAvgChunkSize * (sampleIndex - 1);
+ }
+
+ sampleIndex = sampleStartInBytes / track.mBytesPerSample;
+ }
+
*sampleTimeUs = (sampleIndex * 1000000ll * track.mRate) / track.mScale;
return OK;
@@ -928,8 +1178,24 @@
const Track &track = mTracks.itemAt(trackIndex);
- ssize_t closestSampleIndex =
- timeUs / track.mRate * track.mScale / 1000000ll;
+ ssize_t closestSampleIndex;
+
+ if (track.mBytesPerSample > 0) {
+ size_t closestByteOffset =
+ (timeUs * track.mBytesPerSample)
+ / track.mRate * track.mScale / 1000000ll;
+
+ if (closestByteOffset <= track.mFirstChunkSize) {
+ closestSampleIndex = 0;
+ } else {
+ closestSampleIndex =
+ (closestByteOffset - track.mFirstChunkSize)
+ / track.mAvgChunkSize;
+ }
+ } else {
+ // Each chunk contains a single sample.
+ closestSampleIndex = timeUs / track.mRate * track.mScale / 1000000ll;
+ }
ssize_t numSamples = track.mSamples.size();
diff --git a/media/libstagefright/include/AVIExtractor.h b/media/libstagefright/include/AVIExtractor.h
index 75ce68d..ff5dcb5 100644
--- a/media/libstagefright/include/AVIExtractor.h
+++ b/media/libstagefright/include/AVIExtractor.h
@@ -42,6 +42,7 @@
private:
struct AVISource;
+ struct MP3Splitter;
struct SampleInfo {
uint32_t mOffset;
@@ -70,6 +71,10 @@
size_t mThumbnailSampleSize;
ssize_t mThumbnailSampleIndex;
size_t mMaxSampleSize;
+
+ // If mBytesPerSample > 0:
+ double mAvgChunkSize;
+ size_t mFirstChunkSize;
};
sp<DataSource> mDataSource;
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 6e5f856..113f0f7 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -156,6 +156,7 @@
}
ret.packageName = pkg.packageName;
ret.installLocation = pkg.installLocation;
+ ret.verifiers = pkg.verifiers;
ret.recommendedInstallLocation = recommendAppInstallLocation(pkg.installLocation,
archiveFilePath, flags, threshold);
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home.png
index 3137e7e..908056f 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home.png
index 5c98614..9820a79 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home.png
index 7c6b8e5..0b41317 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home.png
Binary files differ
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java
index 37c77f6..5959537 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java
@@ -118,7 +118,7 @@
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
- PixelFormat.RGBX_8888);
+ PixelFormat.OPAQUE);
// the status bar should be in an overlay if possible
final Display defaultDisplay
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 25b1bc1..e5d4d22 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -316,12 +316,7 @@
}
// figure out which pixel-format to use for the status bar.
- mPixelFormat = PixelFormat.TRANSLUCENT;
- Drawable bg = sb.getBackground();
- if (bg != null) {
- mPixelFormat = bg.getOpacity();
- }
-
+ mPixelFormat = PixelFormat.OPAQUE;
mStatusIcons = (LinearLayout)sb.findViewById(R.id.statusIcons);
mNotificationIcons = (IconMerger)sb.findViewById(R.id.notificationIcons);
mIcons = (LinearLayout)sb.findViewById(R.id.icons);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 0dfc4f7..c83c470 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -720,28 +720,19 @@
Slog.d(TAG, "updateConnectivity: connectionStatus=" + connectionStatus);
}
- int inetCondition = (connectionStatus > INET_CONDITION_THRESHOLD ? 1 : 0);
+ mInetCondition = (connectionStatus > INET_CONDITION_THRESHOLD ? 1 : 0);
- switch (info.getType()) {
- case ConnectivityManager.TYPE_MOBILE:
- mInetCondition = inetCondition;
- updateDataNetType();
- updateDataIcon();
- updateTelephonySignalStrength(); // apply any change in connectionStatus
- break;
- case ConnectivityManager.TYPE_WIFI:
- mInetCondition = inetCondition;
- updateWifiIcons();
- break;
- case ConnectivityManager.TYPE_BLUETOOTH:
- mInetCondition = inetCondition;
- if (info != null) {
- mBluetoothTethered = info.isConnected() ? true: false;
- } else {
- mBluetoothTethered = false;
- }
- break;
+ if (info != null && info.getType() == ConnectivityManager.TYPE_BLUETOOTH) {
+ mBluetoothTethered = info.isConnected() ? true: false;
+ } else {
+ mBluetoothTethered = false;
}
+
+ // We want to update all the icons, all at once, for any condition change
+ updateDataNetType();
+ updateDataIcon();
+ updateTelephonySignalStrength();
+ updateWifiIcons();
}
@@ -1035,8 +1026,8 @@
pw.println(mWifiLevel);
pw.print(" mWifiSsid=");
pw.println(mWifiSsid);
- pw.print(" mWifiIconId=");
- pw.println(mWifiIconId);
+ pw.print(String.format(" mWifiIconId=0x%08x/%s",
+ mWifiIconId, getResourceName(mWifiIconId)));
pw.print(" mWifiActivity=");
pw.println(mWifiActivity);
diff --git a/packages/VpnDialogs/res/layout/confirm.xml b/packages/VpnDialogs/res/layout/confirm.xml
index 11a247a..fef00c2 100644
--- a/packages/VpnDialogs/res/layout/confirm.xml
+++ b/packages/VpnDialogs/res/layout/confirm.xml
@@ -15,46 +15,43 @@
limitations under the License.
-->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="3mm">
-
- <ImageView android:id="@+id/icon"
- android:layout_width="@android:dimen/app_icon_size"
- android:layout_height="@android:dimen/app_icon_size"
- android:layout_alignParentTop="true"
- android:layout_alignParentLeft="true"
- android:layout_marginRight="1mm"/>
-
- <TextView android:id="@+id/warning"
- android:layout_width="fill_parent"
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_alignParentRight="true"
- android:layout_below="@id/icon"
- android:padding="3mm"
- android:text="@string/warning"
- android:textSize="18sp"/>
+ android:orientation="vertical"
+ android:padding="3mm">
- <TextView android:id="@+id/prompt"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:layout_alignParentRight="true"
- android:layout_toRightOf="@id/icon"
- android:layout_above="@id/warning"
- android:gravity="center_vertical"
- android:textSize="20sp"/>
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center_vertical">
- <CheckBox android:id="@+id/check"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_alignParentRight="true"
- android:layout_below="@id/warning"
- android:text="@string/accept"
- android:textSize="20sp"
- android:checked="false"/>
+ <ImageView android:id="@+id/icon"
+ android:layout_width="@android:dimen/app_icon_size"
+ android:layout_height="@android:dimen/app_icon_size"
+ android:paddingRight="1mm"/>
-</RelativeLayout>
+ <TextView android:id="@+id/prompt"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:textSize="18sp"/>
+ </LinearLayout>
+
+ <TextView android:id="@+id/warning"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="1mm"
+ android:paddingBottom="1mm"
+ android:text="@string/warning"
+ android:textSize="18sp"/>
+
+ <CheckBox android:id="@+id/check"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/accept"
+ android:textSize="20sp"
+ android:checked="false"/>
+ </LinearLayout>
+</ScrollView>
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
index d668e98..7fb1417 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
@@ -181,8 +181,10 @@
String line = in.readLine().trim();
if (line.startsWith(prefix)) {
String[] numbers = line.substring(prefix.length()).split(" +");
- if (numbers.length == 17) {
- return numbers;
+ for (int i = 1; i < 17; ++i) {
+ if (!numbers[i].equals("0")) {
+ return numbers;
+ }
}
break;
}
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
index 61e30bf..24dce1a 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
@@ -58,6 +58,7 @@
private static final int CARRIER_HELP_TEXT = 12;
private static final int HELP_MESSAGE_TEXT = 13;
private static final int OWNER_INFO = 14;
+ private static final int BATTERY_INFO = 15;
private StatusMode mStatus;
private String mDateFormatString;
@@ -84,6 +85,9 @@
// last known battery level
private int mBatteryLevel = 100;
+ // last known SIM state
+ protected State mSimState;
+
private LockPatternUtils mLockPatternUtils;
private KeyguardUpdateMonitor mUpdateMonitor;
private Button mEmergencyCallButton;
@@ -98,6 +102,8 @@
private boolean mShowingStatus;
private KeyguardScreenCallback mCallback;
private final boolean mShowEmergencyButtonByDefault;
+ private CharSequence mPlmn;
+ private CharSequence mSpn;
private class TransientTextManager {
private TextView mTextView;
@@ -151,6 +157,7 @@
public KeyguardStatusViewManager(View view, KeyguardUpdateMonitor updateMonitor,
LockPatternUtils lockPatternUtils, KeyguardScreenCallback callback,
boolean showEmergencyButtonByDefault) {
+ if (DEBUG) Log.v(TAG, "KeyguardStatusViewManager()");
mContainer = view;
mDateFormatString = getContext().getString(R.string.full_wday_month_day_no_year);
mLockPatternUtils = lockPatternUtils;
@@ -165,6 +172,12 @@
mTransportView = (TransportControlView) findViewById(R.id.transport);
mEmergencyCallButton = (Button) findViewById(R.id.emergencyCallButton);
mShowEmergencyButtonByDefault = showEmergencyButtonByDefault;
+
+ // Hide transport control view until we know we need to show it.
+ if (mTransportView != null) {
+ mTransportView.setVisibility(View.GONE);
+ }
+
if (mEmergencyCallButton != null) {
mEmergencyCallButton.setText(R.string.lockscreen_emergency_call);
mEmergencyCallButton.setOnClickListener(this);
@@ -173,8 +186,6 @@
mTransientTextManager = new TransientTextManager(mCarrierView);
- updateEmergencyCallButtonState();
-
resetStatusInfo();
refreshDate();
updateOwnerInfo();
@@ -187,10 +198,6 @@
v.setSelected(true);
}
}
-
- // until we get an update...
- setCarrierText(LockPatternUtils.getCarrierString(
- mUpdateMonitor.getTelephonyPlmn(), mUpdateMonitor.getTelephonySpn()));
}
private boolean inWidgetMode() {
@@ -248,6 +255,7 @@
case INSTRUCTION_TEXT:
case CARRIER_HELP_TEXT:
case HELP_MESSAGE_TEXT:
+ case BATTERY_INFO:
mTransientTextManager.post(string, 0, INSTRUCTION_RESET_DELAY);
break;
@@ -262,15 +270,16 @@
}
public void onPause() {
+ if (DEBUG) Log.v(TAG, "onPause()");
mUpdateMonitor.removeCallback(mInfoCallback);
mUpdateMonitor.removeCallback(mSimStateCallback);
}
/** {@inheritDoc} */
public void onResume() {
+ if (DEBUG) Log.v(TAG, "onResume()");
mUpdateMonitor.registerInfoCallback(mInfoCallback);
mUpdateMonitor.registerSimStateCallback(mSimStateCallback);
- updateEmergencyCallButtonState();
resetStatusInfo();
}
@@ -399,7 +408,12 @@
* Determine the current status of the lock screen given the sim state and other stuff.
*/
public StatusMode getStatusForIccState(IccCard.State simState) {
- boolean missingAndNotProvisioned = (!mUpdateMonitor.isDeviceProvisioned()
+ // Since reading the SIM may take a while, we assume it is present until told otherwise.
+ if (simState == null) {
+ return StatusMode.Normal;
+ }
+
+ final boolean missingAndNotProvisioned = (!mUpdateMonitor.isDeviceProvisioned()
&& (simState == IccCard.State.ABSENT || simState == IccCard.State.PERM_DISABLED));
// Assume we're NETWORK_LOCKED if not provisioned
@@ -435,22 +449,21 @@
*
* @param simState
*/
- private void updateWithSimStatus(State simState) {
- // The emergency call button no longer appears on this screen.
- if (DEBUG) Log.d(TAG, "updateLayout: status=" + mStatus);
+ private void updateCarrierTextWithSimStatus(State simState) {
+ if (DEBUG) Log.d(TAG, "updateCarrierTextWithSimStatus(), simState = " + simState);
CharSequence carrierText = null;
int carrierHelpTextId = 0;
mUnlockDisabledDueToSimState = false;
mStatus = getStatusForIccState(simState);
+ mSimState = simState;
switch (mStatus) {
case Normal:
- carrierText = LockPatternUtils.getCarrierString(mUpdateMonitor.getTelephonyPlmn(),
- mUpdateMonitor.getTelephonySpn());
+ carrierText = makeCarierString(mPlmn, mSpn);
break;
case NetworkLocked:
- carrierText = LockPatternUtils.getCarrierString(mUpdateMonitor.getTelephonyPlmn(),
+ carrierText = makeCarierString(mPlmn,
getContext().getText(R.string.lockscreen_network_locked_message));
carrierHelpTextId = R.string.lockscreen_instructions_when_pattern_disabled;
break;
@@ -467,19 +480,19 @@
break;
case SimMissingLocked:
- carrierText = LockPatternUtils.getCarrierString(mUpdateMonitor.getTelephonyPlmn(),
+ carrierText = makeCarierString(mPlmn,
getContext().getText(R.string.lockscreen_missing_sim_message_short));
carrierHelpTextId = R.string.lockscreen_missing_sim_instructions;
mUnlockDisabledDueToSimState = true;
break;
case SimLocked:
- carrierText = LockPatternUtils.getCarrierString(mUpdateMonitor.getTelephonyPlmn(),
+ carrierText = makeCarierString(mPlmn,
getContext().getText(R.string.lockscreen_sim_locked_message));
break;
case SimPukLocked:
- carrierText = LockPatternUtils.getCarrierString(mUpdateMonitor.getTelephonyPlmn(),
+ carrierText = makeCarierString(mPlmn,
getContext().getText(R.string.lockscreen_sim_puk_locked_message));
if (!mLockPatternUtils.isPukUnlockScreenEnable()) {
mUnlockDisabledDueToSimState = true;
@@ -489,7 +502,6 @@
setCarrierText(carrierText);
setCarrierHelpText(carrierHelpTextId);
- updateEmergencyCallButtonState();
}
private View findViewById(int id) {
@@ -552,10 +564,11 @@
}
}
- private void updateEmergencyCallButtonState() {
+ private void updateEmergencyCallButtonState(int phoneState) {
if (mEmergencyCallButton != null) {
boolean showIfCapable = mShowEmergencyButtonByDefault || mUnlockDisabledDueToSimState;
- mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton, showIfCapable);
+ mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton,
+ phoneState, showIfCapable);
}
}
@@ -567,7 +580,8 @@
mShowingBatteryInfo = showBatteryInfo;
mPluggedIn = pluggedIn;
mBatteryLevel = batteryLevel;
- updateStatusLines(true);
+ final MutableInt tmpIcon = new MutableInt(0);
+ update(BATTERY_INFO, getAltTextMessage(tmpIcon));
}
public void onTimeChanged() {
@@ -575,15 +589,17 @@
}
public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {
- setCarrierText(LockPatternUtils.getCarrierString(plmn, spn));
+ mPlmn = plmn;
+ mSpn = spn;
+ updateCarrierTextWithSimStatus(mSimState);
}
public void onRingerModeChanged(int state) {
}
- public void onPhoneStateChanged(String newState) {
- updateEmergencyCallButtonState();
+ public void onPhoneStateChanged(int phoneState) {
+ updateEmergencyCallButtonState(phoneState);
}
/** {@inheritDoc} */
@@ -595,7 +611,7 @@
private SimStateCallback mSimStateCallback = new SimStateCallback() {
public void onSimStateChanged(State simState) {
- updateWithSimStatus(simState);
+ updateCarrierTextWithSimStatus(simState);
}
};
@@ -604,4 +620,22 @@
mCallback.takeEmergencyCallAction();
}
}
+
+ /**
+ * Performs concentenation of PLMN/SPN
+ * @param plmn
+ * @param spn
+ * @return
+ */
+ private static CharSequence makeCarierString(CharSequence plmn, CharSequence spn) {
+ if (plmn != null && spn == null) {
+ return plmn;
+ } else if (plmn != null && spn != null) {
+ return plmn + "|" + spn;
+ } else if (plmn == null && spn != null) {
+ return spn;
+ } else {
+ return "";
+ }
+ }
}
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
index 2a23709..ec24f97 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
@@ -88,6 +88,8 @@
private ArrayList<InfoCallback> mInfoCallbacks = Lists.newArrayList();
private ArrayList<SimStateCallback> mSimStateCallbacks = Lists.newArrayList();
private ContentObserver mContentObserver;
+ private int mRingMode;
+ private int mPhoneState;
// messages for the handler
private static final int MSG_TIME_UPDATE = 301;
@@ -271,13 +273,21 @@
protected void handlePhoneStateChanged(String newState) {
if (DEBUG) Log.d(TAG, "handlePhoneStateChanged(" + newState + ")");
+ if (TelephonyManager.EXTRA_STATE_IDLE.equals(newState)) {
+ mPhoneState = TelephonyManager.CALL_STATE_IDLE;
+ } else if (TelephonyManager.EXTRA_STATE_OFFHOOK.equals(newState)) {
+ mPhoneState = TelephonyManager.CALL_STATE_OFFHOOK;
+ } else if (TelephonyManager.EXTRA_STATE_RINGING.equals(newState)) {
+ mPhoneState = TelephonyManager.CALL_STATE_RINGING;
+ }
for (int i = 0; i < mInfoCallbacks.size(); i++) {
- mInfoCallbacks.get(i).onPhoneStateChanged(newState);
+ mInfoCallbacks.get(i).onPhoneStateChanged(mPhoneState);
}
}
protected void handleRingerModeChange(int mode) {
if (DEBUG) Log.d(TAG, "handleRingerModeChange(" + mode + ")");
+ mRingMode = mode;
for (int i = 0; i < mInfoCallbacks.size(); i++) {
mInfoCallbacks.get(i).onRingerModeChanged(mode);
}
@@ -459,7 +469,7 @@
* {@link TelephonyManager@EXTRA_STATE_RINGING}
* {@link TelephonyManager#EXTRA_STATE_OFFHOOK
*/
- void onPhoneStateChanged(String newState);
+ void onPhoneStateChanged(int phoneState);
/**
* Called when visibility of lockscreen clock changes, such as when
@@ -484,8 +494,12 @@
public void registerInfoCallback(InfoCallback callback) {
if (!mInfoCallbacks.contains(callback)) {
mInfoCallbacks.add(callback);
- // notify the register the current state right away
- // TODO: need call other callback methods
+ // Notify listener of the current state
+ callback.onRefreshBatteryInfo(shouldShowBatteryInfo(), isPluggedIn(mBatteryStatus),
+ mBatteryLevel);
+ callback.onTimeChanged();
+ callback.onRingerModeChanged(mRingMode);
+ callback.onPhoneStateChanged(mPhoneState);
callback.onRefreshCarrierInfo(mTelephonyPlmn, mTelephonySpn);
} else {
if (DEBUG) Log.e(TAG, "Object tried to add another INFO callback",
@@ -500,9 +514,7 @@
public void registerSimStateCallback(SimStateCallback callback) {
if (!mSimStateCallbacks.contains(callback)) {
mSimStateCallbacks.add(callback);
- // notify the register the current sim state right away,
- // otherwise the register won't receive any state until
- // sim state gets changed again.
+ // Notify listener of the current state
callback.onSimStateChanged(mSimState);
} else {
if (DEBUG) Log.e(TAG, "Object tried to add another SIM callback",
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
index 0f1d633..3dae5ad 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
@@ -766,7 +766,7 @@
// Don't play lockscreen SFX if the screen went off due to
// timeout.
mSuppressNextLockSound = true;
-
+
doKeyguardLocked();
}
}
@@ -777,7 +777,7 @@
if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState) // call ending
&& !mScreenOn // screen off
&& mExternallyEnabled) { // not disabled by any app
-
+
// note: this is a way to gracefully reenable the keyguard when the call
// ends and the screen is off without always reenabling the keyguard
// each time the screen turns off while in call (and having an occasional ugly
@@ -1270,7 +1270,7 @@
}
/** {@inheritDoc} */
- public void onPhoneStateChanged(String newState) {
+ public void onPhoneStateChanged(int phoneState) {
// ignored
}
diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
index d9bd5f2b..ffb4838 100644
--- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
+++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
@@ -114,6 +114,10 @@
private final int MSG_SHOW_FACELOCK_AREA_VIEW = 0;
private final int MSG_HIDE_FACELOCK_AREA_VIEW = 1;
+ // Long enough to stay black while dialer comes up
+ // Short enough to not be black if the user goes back immediately
+ private final int FACELOCK_VIEW_AREA_EMERGENCY_HIDE_TIMEOUT = 1000;
+
/**
* The current {@link KeyguardScreen} will use this to communicate back to us.
*/
@@ -311,6 +315,13 @@
}
public void takeEmergencyCallAction() {
+ // FaceLock must be stopped if it is running when emergency call is pressed
+ stopAndUnbindFromFaceLock();
+
+ // Delay hiding FaceLock area so unlock doesn't display while dialer is coming up
+ mHandler.sendEmptyMessageDelayed(MSG_HIDE_FACELOCK_AREA_VIEW,
+ FACELOCK_VIEW_AREA_EMERGENCY_HIDE_TIMEOUT);
+
pokeWakelock(EMERGENCY_CALL_TIMEOUT);
if (TelephonyManager.getDefault().getCallState()
== TelephonyManager.CALL_STATE_OFFHOOK) {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index ee73ed1..968180c 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -446,9 +446,8 @@
}
@Override
- public void onOrientationChanged(int rotation) {
- // Send updates based on orientation value
- if (localLOGV) Log.v(TAG, "onOrientationChanged, rotation changed to " +rotation);
+ public void onProposedRotationChanged(int rotation) {
+ if (localLOGV) Log.v(TAG, "onProposedRotationChanged, rotation=" + rotation);
updateRotation(false);
}
}
@@ -654,6 +653,9 @@
mKeyguardMediator = new KeyguardViewMediator(context, this, powerManager);
mHandler = new Handler();
mOrientationListener = new MyOrientationListener(mContext);
+ try {
+ mOrientationListener.setCurrentRotation(windowManager.getRotation());
+ } catch (RemoteException ex) { }
SettingsObserver settingsObserver = new SettingsObserver(mHandler);
settingsObserver.observe();
mShortcutManager = new ShortcutManager(context, mHandler);
@@ -2912,7 +2914,10 @@
}
synchronized (mLock) {
- int sensorRotation = mOrientationListener.getCurrentRotation(); // may be -1
+ int sensorRotation = mOrientationListener.getProposedRotation(); // may be -1
+ if (sensorRotation < 0) {
+ sensorRotation = lastRotation;
+ }
int preferredRotation = -1;
if (mHdmiPlugged) {
@@ -2922,20 +2927,18 @@
// Ignore sensor when lid switch is open and rotation is forced.
preferredRotation = mLidOpenRotation;
} else if (mDockMode == Intent.EXTRA_DOCK_STATE_CAR
- && ((mCarDockEnablesAccelerometer && sensorRotation >= 0)
- || mCarDockRotation >= 0)) {
+ && (mCarDockEnablesAccelerometer || mCarDockRotation >= 0)) {
// Ignore sensor when in car dock unless explicitly enabled.
// This case can override the behavior of NOSENSOR, and can also
// enable 180 degree rotation while docked.
- preferredRotation = mCarDockEnablesAccelerometer && sensorRotation >= 0
+ preferredRotation = mCarDockEnablesAccelerometer
? sensorRotation : mCarDockRotation;
} else if (mDockMode == Intent.EXTRA_DOCK_STATE_DESK
- && ((mDeskDockEnablesAccelerometer && sensorRotation >= 0)
- || mDeskDockRotation >= 0)) {
+ && (mDeskDockEnablesAccelerometer || mDeskDockRotation >= 0)) {
// Ignore sensor when in desk dock unless explicitly enabled.
// This case can override the behavior of NOSENSOR, and can also
// enable 180 degree rotation while docked.
- preferredRotation = mDeskDockEnablesAccelerometer && sensorRotation >= 0
+ preferredRotation = mDeskDockEnablesAccelerometer
? sensorRotation : mDeskDockRotation;
} else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) {
// Ignore sensor when user locked rotation.
@@ -3036,6 +3039,11 @@
}
}
+ @Override
+ public void setRotationLw(int rotation) {
+ mOrientationListener.setCurrentRotation(rotation);
+ }
+
private boolean isLandscapeOrSeascape(int rotation) {
return rotation == mLandscapeRotation || rotation == mSeascapeRotation;
}
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 6ac6c98..e30ce72 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -3380,7 +3380,8 @@
Uri packageUri = Uri.fromFile(apkFile);
mInstallObserver.reset();
mPackageManager.installPackage(packageUri, mInstallObserver,
- PackageManager.INSTALL_REPLACE_EXISTING, installerPackage);
+ PackageManager.INSTALL_REPLACE_EXISTING | PackageManager.INSTALL_FROM_ADB,
+ installerPackage);
mInstallObserver.waitForCompletion();
if (mInstallObserver.getResult() != PackageManager.INSTALL_SUCCEEDED) {
@@ -4373,8 +4374,13 @@
ParcelFileDescriptor.MODE_TRUNCATE);
if (mTransport.getRestoreData(mBackupData) != BackupConstants.TRANSPORT_OK) {
+ // Transport-level failure, so we wind everything up and
+ // terminate the restore operation.
Slog.e(TAG, "Error getting restore data for " + packageName);
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
+ mBackupData.close();
+ mBackupDataName.delete();
+ executeNextState(RestoreState.FINAL);
return;
}
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 327450a..991b7da 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -35,6 +35,7 @@
import android.net.IConnectivityManager;
import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
+import android.net.INetworkStatsService;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.LinkProperties.CompareResult;
@@ -306,8 +307,8 @@
// the set of network types that can only be enabled by system/sig apps
List mProtectedNetworks;
- public ConnectivityService(
- Context context, INetworkManagementService netd, INetworkPolicyManager policyManager) {
+ public ConnectivityService(Context context, INetworkManagementService netd,
+ INetworkStatsService statsService, INetworkPolicyManager policyManager) {
if (DBG) log("ConnectivityService starting up");
HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread");
@@ -496,7 +497,7 @@
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService nmService = INetworkManagementService.Stub.asInterface(b);
- mTethering = new Tethering(mContext, nmService, mHandler.getLooper());
+ mTethering = new Tethering(mContext, nmService, statsService, mHandler.getLooper());
mTetheringConfigValid = ((mTethering.getTetherableUsbRegexs().length != 0 ||
mTethering.getTetherableWifiRegexs().length != 0 ||
mTethering.getTetherableBluetoothRegexs().length != 0) &&
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 977dd6f..5006de7 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -65,6 +65,7 @@
class ServerThread extends Thread {
private static final String TAG = "SystemServer";
private static final String ENCRYPTING_STATE = "trigger_restart_min_framework";
+ private static final String ENCRYPTED_STATE = "1";
ContentResolver mContentResolver;
@@ -150,10 +151,15 @@
Slog.i(TAG, "Package Manager");
// Only run "core" apps if we're encrypting the device.
String cryptState = SystemProperties.get("vold.decrypt");
- boolean onlyCore = ENCRYPTING_STATE.equals(cryptState);
- if (onlyCore) {
+ boolean onlyCore = false;
+ if (ENCRYPTING_STATE.equals(cryptState)) {
Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
+ onlyCore = true;
+ } else if (ENCRYPTED_STATE.equals(cryptState)) {
+ Slog.w(TAG, "Device encrypted - only parsing core apps");
+ onlyCore = true;
}
+
pm = PackageManagerService.main(context,
factoryTest != SystemServer.FACTORY_TEST_OFF,
onlyCore);
@@ -364,7 +370,8 @@
try {
Slog.i(TAG, "Connectivity Service");
- connectivity = new ConnectivityService(context, networkManagement, networkPolicy);
+ connectivity = new ConnectivityService(
+ context, networkManagement, networkStats, networkPolicy);
ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);
networkStats.bindConnectivityManager(connectivity);
networkPolicy.bindConnectivityManager(connectivity);
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 10f6d2c..6b9c088 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -29,6 +29,7 @@
import android.net.ConnectivityManager;
import android.net.IConnectivityManager;
import android.net.INetworkManagementEventObserver;
+import android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -88,7 +89,8 @@
// upstream type list and the DUN_REQUIRED secure-setting
private int mPreferredUpstreamMobileApn = ConnectivityManager.TYPE_NONE;
- private INetworkManagementService mNMService;
+ private final INetworkManagementService mNMService;
+ private final INetworkStatsService mStatsService;
private Looper mLooper;
private HandlerThread mThread;
@@ -124,9 +126,11 @@
private boolean mUsbTetherRequested; // true if USB tethering should be started
// when RNDIS is enabled
- public Tethering(Context context, INetworkManagementService nmService, Looper looper) {
+ public Tethering(Context context, INetworkManagementService nmService,
+ INetworkStatsService statsService, Looper looper) {
mContext = context;
mNMService = nmService;
+ mStatsService = statsService;
mLooper = looper;
mIfaces = new HashMap<String, TetherInterfaceSM>();
@@ -913,6 +917,9 @@
case CMD_INTERFACE_DOWN:
if (mMyUpstreamIfaceName != null) {
try {
+ // about to tear down NAT; gather remaining statistics
+ mStatsService.forceUpdate();
+
mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName);
mMyUpstreamIfaceName = null;
} catch (Exception e) {
@@ -957,6 +964,9 @@
}
if (mMyUpstreamIfaceName != null) {
try {
+ // about to tear down NAT; gather remaining statistics
+ mStatsService.forceUpdate();
+
mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName);
mMyUpstreamIfaceName = null;
} catch (Exception e) {
@@ -995,6 +1005,9 @@
case CMD_TETHER_MODE_DEAD:
if (mMyUpstreamIfaceName != null) {
try {
+ // about to tear down NAT; gather remaining statistics
+ mStatsService.forceUpdate();
+
mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName);
mMyUpstreamIfaceName = null;
} catch (Exception e) {
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index bc65205..aa46795 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -24,8 +24,8 @@
import static android.content.Intent.ACTION_SHUTDOWN;
import static android.content.Intent.ACTION_UID_REMOVED;
import static android.content.Intent.EXTRA_UID;
-import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED;
+import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.SET_ALL;
import static android.net.NetworkStats.SET_DEFAULT;
@@ -43,9 +43,12 @@
import static android.provider.Settings.Secure.NETSTATS_TAG_MAX_HISTORY;
import static android.provider.Settings.Secure.NETSTATS_UID_BUCKET_DURATION;
import static android.provider.Settings.Secure.NETSTATS_UID_MAX_HISTORY;
+import static android.telephony.PhoneStateListener.LISTEN_DATA_CONNECTION_STATE;
+import static android.telephony.PhoneStateListener.LISTEN_NONE;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+import static android.text.format.DateUtils.SECOND_IN_MILLIS;
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
import static com.android.server.NetworkManagementSocketTagger.resetKernelUidStats;
@@ -80,6 +83,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.provider.Settings;
+import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.EventLog;
import android.util.Log;
@@ -121,7 +125,7 @@
*/
public class NetworkStatsService extends INetworkStatsService.Stub {
private static final String TAG = "NetworkStats";
- private static final boolean LOGD = true;
+ private static final boolean LOGD = false;
private static final boolean LOGV = false;
/** File header magic number: "ANET" */
@@ -132,7 +136,8 @@
private static final int VERSION_UID_WITH_TAG = 3;
private static final int VERSION_UID_WITH_SET = 4;
- private static final int MSG_PERFORM_POLL = 0x1;
+ private static final int MSG_PERFORM_POLL = 1;
+ private static final int MSG_UPDATE_IFACES = 2;
/** Flags to control detail level of poll event. */
private static final int FLAG_PERSIST_NETWORK = 0x10;
@@ -144,6 +149,7 @@
private final INetworkManagementService mNetworkManager;
private final IAlarmManager mAlarmManager;
private final TrustedTime mTime;
+ private final TelephonyManager mTeleManager;
private final NetworkStatsSettings mSettings;
private final PowerManager.WakeLock mWakeLock;
@@ -227,6 +233,7 @@
mNetworkManager = checkNotNull(networkManager, "missing INetworkManagementService");
mAlarmManager = checkNotNull(alarmManager, "missing IAlarmManager");
mTime = checkNotNull(time, "missing TrustedTime");
+ mTeleManager = checkNotNull(TelephonyManager.getDefault(), "missing TelephonyManager");
mSettings = checkNotNull(settings, "missing NetworkStatsSettings");
final PowerManager powerManager = (PowerManager) context.getSystemService(
@@ -279,6 +286,10 @@
// ignored; service lives in system_server
}
+ // watch for networkType changes that aren't broadcast through
+ // CONNECTIVITY_ACTION_IMMEDIATE above.
+ mTeleManager.listen(mPhoneListener, LISTEN_DATA_CONNECTION_STATE);
+
registerPollAlarmLocked();
registerGlobalAlert();
@@ -288,10 +299,13 @@
private void shutdownLocked() {
mContext.unregisterReceiver(mConnReceiver);
+ mContext.unregisterReceiver(mTetherReceiver);
mContext.unregisterReceiver(mPollReceiver);
mContext.unregisterReceiver(mRemovedReceiver);
mContext.unregisterReceiver(mShutdownReceiver);
+ mTeleManager.listen(mPhoneListener, LISTEN_NONE);
+
writeNetworkStatsLocked();
if (mUidStatsLoaded) {
writeUidStatsLocked();
@@ -535,14 +549,7 @@
public void onReceive(Context context, Intent intent) {
// on background handler thread, and verified CONNECTIVITY_INTERNAL
// permission above.
- synchronized (mStatsLock) {
- mWakeLock.acquire();
- try {
- updateIfacesLocked();
- } finally {
- mWakeLock.release();
- }
- }
+ updateIfaces();
}
};
@@ -619,6 +626,46 @@
}
};
+ private int mLastPhoneState = TelephonyManager.DATA_UNKNOWN;
+ private int mLastPhoneNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+
+ /**
+ * Receiver that watches for {@link TelephonyManager} changes, such as
+ * transitioning between network types.
+ */
+ private PhoneStateListener mPhoneListener = new PhoneStateListener() {
+ @Override
+ public void onDataConnectionStateChanged(int state, int networkType) {
+ final boolean stateChanged = state != mLastPhoneState;
+ final boolean networkTypeChanged = networkType != mLastPhoneNetworkType;
+
+ if (networkTypeChanged && !stateChanged) {
+ // networkType changed without a state change, which means we
+ // need to roll our own update. delay long enough for
+ // ConnectivityManager to process.
+ // TODO: add direct event to ConnectivityService instead of
+ // relying on this delay.
+ if (LOGV) Slog.v(TAG, "triggering delayed updateIfaces()");
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(MSG_UPDATE_IFACES), SECOND_IN_MILLIS);
+ }
+
+ mLastPhoneState = state;
+ mLastPhoneNetworkType = networkType;
+ }
+ };
+
+ private void updateIfaces() {
+ synchronized (mStatsLock) {
+ mWakeLock.acquire();
+ try {
+ updateIfacesLocked();
+ } finally {
+ mWakeLock.release();
+ }
+ }
+ }
+
/**
* Inspect all current {@link NetworkState} to derive mapping from {@code
* iface} to {@link NetworkStatsHistory}. When multiple {@link NetworkInfo}
@@ -713,19 +760,6 @@
final long threshold = mSettings.getPersistThreshold();
try {
- // record network stats
- final NetworkStats networkSnapshot = mNetworkManager.getNetworkStatsSummary();
- performNetworkPollLocked(networkSnapshot, currentTime);
-
- // persist when enough network data has occurred
- final NetworkStats persistNetworkDelta = computeStatsDelta(
- mLastPersistNetworkSnapshot, networkSnapshot, true);
- final boolean networkPastThreshold = persistNetworkDelta.getTotalBytes() > threshold;
- if (persistForce || (persistNetwork && networkPastThreshold)) {
- writeNetworkStatsLocked();
- mLastPersistNetworkSnapshot = networkSnapshot;
- }
-
// record tethering stats; persisted during normal UID cycle below
final String[] ifacePairs = mConnManager.getTetheredIfacePairs();
final NetworkStats tetherSnapshot = mNetworkManager.getNetworkStatsTethering(
@@ -744,6 +778,19 @@
writeUidStatsLocked();
mLastPersistUidSnapshot = uidSnapshot;
}
+
+ // record network stats
+ final NetworkStats networkSnapshot = mNetworkManager.getNetworkStatsSummary();
+ performNetworkPollLocked(networkSnapshot, currentTime);
+
+ // persist when enough network data has occurred
+ final NetworkStats persistNetworkDelta = computeStatsDelta(
+ mLastPersistNetworkSnapshot, networkSnapshot, true);
+ final boolean networkPastThreshold = persistNetworkDelta.getTotalBytes() > threshold;
+ if (persistForce || (persistNetwork && networkPastThreshold)) {
+ writeNetworkStatsLocked();
+ mLastPersistNetworkSnapshot = networkSnapshot;
+ }
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem reading network stats", e);
} catch (RemoteException e) {
@@ -1356,6 +1403,10 @@
performPoll(flags);
return true;
}
+ case MSG_UPDATE_IFACES: {
+ updateIfaces();
+ return true;
+ }
default: {
return false;
}
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 05f7cf0..eb135b7 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -72,6 +72,7 @@
import android.content.pm.UserInfo;
import android.content.pm.ManifestDigest;
import android.content.pm.VerifierDeviceIdentity;
+import android.content.pm.VerifierInfo;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -113,6 +114,8 @@
import java.io.InputStream;
import java.io.PrintWriter;
import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.cert.CertificateException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -158,6 +161,7 @@
private static final boolean DEBUG_INTENT_MATCHING = false;
private static final boolean DEBUG_PACKAGE_SCANNING = false;
private static final boolean DEBUG_APP_DIR_OBSERVER = false;
+ private static final boolean DEBUG_VERIFY = false;
static final boolean MULTIPLE_APPLICATION_UIDS = true;
private static final int RADIO_UID = Process.PHONE_UID;
@@ -208,6 +212,8 @@
DEFAULT_CONTAINER_PACKAGE,
"com.android.defcontainer.DefaultContainerService");
+ private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
+
private static final String LIB_DIR_NAME = "lib";
static final String mTempContainerPrefix = "smdl2tmp";
@@ -349,7 +355,8 @@
final HashSet<String> mProtectedBroadcasts = new HashSet<String>();
/** List of packages waiting for verification. */
- final SparseArray<InstallArgs> mPendingVerification = new SparseArray<InstallArgs>();
+ final SparseArray<PackageVerificationState> mPendingVerification
+ = new SparseArray<PackageVerificationState>();
final ArrayList<PackageParser.Package> mDeferredDexOpt =
new ArrayList<PackageParser.Package>();
@@ -427,6 +434,8 @@
final SparseArray<PostInstallData> mRunningInstalls = new SparseArray<PostInstallData>();
int mNextInstallToken = 1; // nonzero; will be wrapped back to 1 when ++ overflows
+ private final String mRequiredVerifierPackage;
+
class PackageHandler extends Handler {
private boolean mBound = false;
final ArrayList<HandlerParams> mPendingInstalls =
@@ -740,9 +749,10 @@
} break;
case CHECK_PENDING_VERIFICATION: {
final int verificationId = msg.arg1;
- final InstallArgs args = mPendingVerification.get(verificationId);
+ final PackageVerificationState state = mPendingVerification.get(verificationId);
- if (args != null) {
+ if (state != null) {
+ final InstallArgs args = state.getInstallArgs();
Slog.i(TAG, "Validation timed out for " + args.packageURI.toString());
mPendingVerification.remove(verificationId);
@@ -756,32 +766,39 @@
}
case PACKAGE_VERIFIED: {
final int verificationId = msg.arg1;
- final boolean verified = msg.arg2 == 1 ? true : false;
- final InstallArgs args = mPendingVerification.get(verificationId);
- if (args == null) {
+ final PackageVerificationState state = mPendingVerification.get(verificationId);
+ if (state == null) {
Slog.w(TAG, "Invalid validation token " + verificationId + " received");
break;
}
- mPendingVerification.remove(verificationId);
+ final PackageVerificationResponse response = (PackageVerificationResponse) msg.obj;
- int ret;
- if (verified) {
- ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
- try {
- ret = args.copyApk(mContainerService, true);
- } catch (RemoteException e) {
- Slog.e(TAG, "Could not contact the ContainerService");
+ state.setVerifierResponse(response.callerUid, response.code);
+
+ if (state.isVerificationComplete()) {
+ mPendingVerification.remove(verificationId);
+
+ final InstallArgs args = state.getInstallArgs();
+
+ int ret;
+ if (state.isInstallAllowed()) {
+ ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+ try {
+ ret = args.copyApk(mContainerService, true);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not contact the ContainerService");
+ }
+ } else {
+ ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
}
- } else {
- ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
+
+ processPendingInstall(args, ret);
+
+ mHandler.sendEmptyMessage(MCS_UNBIND);
}
- processPendingInstall(args, ret);
-
- mHandler.sendEmptyMessage(MCS_UNBIND);
-
break;
}
}
@@ -1134,10 +1151,49 @@
// are all flushed. Not really needed, but keeps things nice and
// tidy.
Runtime.getRuntime().gc();
+
+ mRequiredVerifierPackage = getRequiredVerifierLPr();
} // synchronized (mPackages)
} // synchronized (mInstallLock)
}
+ private String getRequiredVerifierLPr() {
+ final Intent verification = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
+ final List<ResolveInfo> receivers = queryIntentReceivers(verification, PACKAGE_MIME_TYPE,
+ PackageManager.GET_DISABLED_COMPONENTS);
+
+ String requiredVerifier = null;
+
+ final int N = receivers.size();
+ for (int i = 0; i < N; i++) {
+ final ResolveInfo info = receivers.get(i);
+
+ if (info.activityInfo == null) {
+ continue;
+ }
+
+ final String packageName = info.activityInfo.packageName;
+
+ final PackageSetting ps = mSettings.mPackages.get(packageName);
+ if (ps == null) {
+ continue;
+ }
+
+ if (!ps.grantedPermissions
+ .contains(android.Manifest.permission.PACKAGE_VERIFICATION_AGENT)) {
+ continue;
+ }
+
+ if (requiredVerifier != null) {
+ throw new RuntimeException("There can be only one required verifier");
+ }
+
+ requiredVerifier = packageName;
+ }
+
+ return requiredVerifier;
+ }
+
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
@@ -4857,17 +4913,110 @@
}
@Override
- public void verifyPendingInstall(int id, int verificationCode)
- throws RemoteException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.PACKAGE_VERIFICATION_AGENT, null);
-
+ public void verifyPendingInstall(int id, int verificationCode) throws RemoteException {
final Message msg = mHandler.obtainMessage(PACKAGE_VERIFIED);
+ final PackageVerificationResponse response = new PackageVerificationResponse(
+ verificationCode, Binder.getCallingUid());
msg.arg1 = id;
- msg.arg2 = verificationCode;
+ msg.obj = response;
mHandler.sendMessage(msg);
}
+ private ComponentName matchComponentForVerifier(String packageName,
+ List<ResolveInfo> receivers) {
+ ActivityInfo targetReceiver = null;
+
+ final int NR = receivers.size();
+ for (int i = 0; i < NR; i++) {
+ final ResolveInfo info = receivers.get(i);
+ if (info.activityInfo == null) {
+ continue;
+ }
+
+ if (packageName.equals(info.activityInfo.packageName)) {
+ targetReceiver = info.activityInfo;
+ break;
+ }
+ }
+
+ if (targetReceiver == null) {
+ return null;
+ }
+
+ return new ComponentName(targetReceiver.packageName, targetReceiver.name);
+ }
+
+ private List<ComponentName> matchVerifiers(PackageInfoLite pkgInfo,
+ List<ResolveInfo> receivers, final PackageVerificationState verificationState) {
+ if (pkgInfo.verifiers.length == 0) {
+ return null;
+ }
+
+ final int N = pkgInfo.verifiers.length;
+ final List<ComponentName> sufficientVerifiers = new ArrayList<ComponentName>(N + 1);
+ for (int i = 0; i < N; i++) {
+ final VerifierInfo verifierInfo = pkgInfo.verifiers[i];
+
+ final ComponentName comp = matchComponentForVerifier(verifierInfo.packageName,
+ receivers);
+ if (comp == null) {
+ continue;
+ }
+
+ final int verifierUid = getUidForVerifier(verifierInfo);
+ if (verifierUid == -1) {
+ continue;
+ }
+
+ if (DEBUG_VERIFY) {
+ Slog.d(TAG, "Added sufficient verifier " + verifierInfo.packageName
+ + " with the correct signature");
+ }
+ sufficientVerifiers.add(comp);
+ verificationState.addSufficientVerifier(verifierUid);
+ }
+
+ return sufficientVerifiers;
+ }
+
+ private int getUidForVerifier(VerifierInfo verifierInfo) {
+ synchronized (mPackages) {
+ final PackageParser.Package pkg = mPackages.get(verifierInfo.packageName);
+ if (pkg == null) {
+ return -1;
+ } else if (pkg.mSignatures.length != 1) {
+ Slog.i(TAG, "Verifier package " + verifierInfo.packageName
+ + " has more than one signature; ignoring");
+ return -1;
+ }
+
+ /*
+ * If the public key of the package's signature does not match
+ * our expected public key, then this is a different package and
+ * we should skip.
+ */
+
+ final byte[] expectedPublicKey;
+ try {
+ final Signature verifierSig = pkg.mSignatures[0];
+ final PublicKey publicKey = verifierSig.getPublicKey();
+ expectedPublicKey = publicKey.getEncoded();
+ } catch (CertificateException e) {
+ return -1;
+ }
+
+ final byte[] actualPublicKey = verifierInfo.publicKey.getEncoded();
+
+ if (!Arrays.equals(actualPublicKey, expectedPublicKey)) {
+ Slog.i(TAG, "Verifier package " + verifierInfo.packageName
+ + " does not have the expected public key; ignoring");
+ return -1;
+ }
+
+ return pkg.applicationInfo.uid;
+ }
+ }
+
public void finishPackageInstall(int token) {
enforceSystemOrRoot("Only the system is allowed to finish installs");
@@ -5237,9 +5386,11 @@
*/
public void handleStartCopy() throws RemoteException {
int ret = PackageManager.INSTALL_SUCCEEDED;
- boolean fwdLocked = (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
- boolean onSd = (flags & PackageManager.INSTALL_EXTERNAL) != 0;
- boolean onInt = (flags & PackageManager.INSTALL_INTERNAL) != 0;
+ final boolean fwdLocked = (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
+ final boolean onSd = (flags & PackageManager.INSTALL_EXTERNAL) != 0;
+ final boolean onInt = (flags & PackageManager.INSTALL_INTERNAL) != 0;
+ PackageInfoLite pkgLite = null;
+
if (onInt && onSd) {
// Check if both bits are set.
Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
@@ -5261,7 +5412,6 @@
}
// Remote call to find out default install location
- final PackageInfoLite pkgLite;
try {
mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,
Intent.FLAG_GRANT_READ_URI_PERMISSION);
@@ -5304,21 +5454,27 @@
}
final InstallArgs args = createInstallArgs(this);
+ mArgs = args;
+
if (ret == PackageManager.INSTALL_SUCCEEDED) {
/*
* Determine if we have any installed package verifiers. If we
* do, then we'll defer to them to verify the packages.
*/
- final Intent verification = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION,
- packageURI);
- verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ final int requiredUid = mRequiredVerifierPackage == null ? -1
+ : getPackageUid(mRequiredVerifierPackage);
+ if (requiredUid != -1 && isVerificationEnabled()) {
+ final Intent verification = new Intent(
+ Intent.ACTION_PACKAGE_NEEDS_VERIFICATION, packageURI);
+ verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- final List<ResolveInfo> receivers = queryIntentReceivers(verification, null,
- PackageManager.GET_DISABLED_COMPONENTS);
- if (isVerificationEnabled() && receivers.size() > 0) {
- if (DEBUG_INSTALL) {
+ final List<ResolveInfo> receivers = queryIntentReceivers(verification, null,
+ PackageManager.GET_DISABLED_COMPONENTS);
+
+ if (DEBUG_VERIFY) {
Slog.d(TAG, "Found " + receivers.size() + " verifiers for intent "
- + verification.toString());
+ + verification.toString() + " with " + pkgLite.verifiers.length
+ + " optional verifiers");
}
final int verificationId = mPendingVerificationToken++;
@@ -5335,35 +5491,70 @@
verificationURI);
}
- mPendingVerification.append(verificationId, args);
+ final PackageVerificationState verificationState = new PackageVerificationState(
+ requiredUid, args);
+
+ mPendingVerification.append(verificationId, verificationState);
+
+ final List<ComponentName> sufficientVerifiers = matchVerifiers(pkgLite,
+ receivers, verificationState);
/*
- * Send the intent to the registered verification agents,
- * but only start the verification timeout after the target
- * BroadcastReceivers have run.
+ * If any sufficient verifiers were listed in the package
+ * manifest, attempt to ask them.
*/
- mContext.sendOrderedBroadcast(verification,
- android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final Message msg = mHandler
- .obtainMessage(CHECK_PENDING_VERIFICATION);
- msg.arg1 = verificationId;
- mHandler.sendMessageDelayed(msg, getVerificationTimeout());
- }
- },
- null, 0, null, null);
+ if (sufficientVerifiers != null) {
+ final int N = sufficientVerifiers.size();
+ if (N == 0) {
+ Slog.i(TAG, "Additional verifiers required, but none installed.");
+ ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
+ } else {
+ for (int i = 0; i < N; i++) {
+ final ComponentName verifierComponent = sufficientVerifiers.get(i);
+
+ final Intent sufficientIntent = new Intent(verification);
+ sufficientIntent.setComponent(verifierComponent);
+
+ mContext.sendBroadcast(sufficientIntent);
+ }
+ }
+ }
+
+ final ComponentName requiredVerifierComponent = matchComponentForVerifier(
+ mRequiredVerifierPackage, receivers);
+ if (ret == PackageManager.INSTALL_SUCCEEDED
+ && mRequiredVerifierPackage != null) {
+ /*
+ * Send the intent to the required verification agent,
+ * but only start the verification timeout after the
+ * target BroadcastReceivers have run.
+ */
+ verification.setComponent(requiredVerifierComponent);
+ mContext.sendOrderedBroadcast(verification,
+ android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final Message msg = mHandler
+ .obtainMessage(CHECK_PENDING_VERIFICATION);
+ msg.arg1 = verificationId;
+ mHandler.sendMessageDelayed(msg, getVerificationTimeout());
+ }
+ }, null, 0, null, null);
+
+ /*
+ * We don't want the copy to proceed until verification
+ * succeeds, so null out this field.
+ */
+ mArgs = null;
+ }
} else {
- // Create copy only if we are not in an erroneous state.
- // Remote call to initiate copy using temporary file
- mArgs = args;
+ /*
+ * No package verification is enabled, so immediately start
+ * the remote call to initiate copy using temporary file.
+ */
ret = args.copyApk(mContainerService, true);
}
- } else {
- // There was an error, so let the processPendingInstall() break
- // the bad news... uh, through a call in handleReturnCode()
- mArgs = args;
}
mRet = ret;
@@ -7549,6 +7740,8 @@
public static final int DUMP_PROVIDERS = 1 << 7;
+ public static final int DUMP_VERIFIERS = 1 << 8;
+
public static final int OPTION_SHOW_FILTERS = 1 << 0;
private int mTypes;
@@ -7641,6 +7834,7 @@
pw.println(" p[ackages]: dump installed packages");
pw.println(" s[hared-users]: dump shared user IDs");
pw.println(" m[essages]: print collected runtime messages");
+ pw.println(" v[erifiers]: print package verifier info");
pw.println(" <package.name>: info about given package");
return;
} else if ("-f".equals(opt)) {
@@ -7673,11 +7867,24 @@
dumpState.setDump(DumpState.DUMP_PROVIDERS);
} else if ("m".equals(cmd) || "messages".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_MESSAGES);
+ } else if ("v".equals(cmd) || "verifiers".equals(cmd)) {
+ dumpState.setDump(DumpState.DUMP_VERIFIERS);
}
}
// reader
synchronized (mPackages) {
+ if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) {
+ if (dumpState.onTitlePrinted())
+ pw.println(" ");
+ pw.println("Verifiers:");
+ pw.print(" Required: ");
+ pw.print(mRequiredVerifierPackage);
+ pw.print(" (uid=");
+ pw.print(getPackageUid(mRequiredVerifierPackage));
+ pw.println(")");
+ }
+
if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) {
if (dumpState.onTitlePrinted())
pw.println(" ");
diff --git a/services/java/com/android/server/pm/PackageVerificationResponse.java b/services/java/com/android/server/pm/PackageVerificationResponse.java
new file mode 100644
index 0000000..b2ae0dd
--- /dev/null
+++ b/services/java/com/android/server/pm/PackageVerificationResponse.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2011 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 com.android.server.pm;
+
+public class PackageVerificationResponse {
+ public final int code;
+
+ public final int callerUid;
+
+ public PackageVerificationResponse(int code, int callerUid) {
+ this.code = code;
+ this.callerUid = callerUid;
+ }
+}
diff --git a/services/java/com/android/server/pm/PackageVerificationState.java b/services/java/com/android/server/pm/PackageVerificationState.java
new file mode 100644
index 0000000..e5b89c1
--- /dev/null
+++ b/services/java/com/android/server/pm/PackageVerificationState.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2011 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 com.android.server.pm;
+
+import com.android.server.pm.PackageManagerService.InstallArgs;
+
+import android.content.pm.PackageManager;
+import android.util.SparseBooleanArray;
+
+/**
+ * Tracks the package verification state for a particular package. Each package
+ * verification has a required verifier and zero or more sufficient verifiers.
+ * Only one of the sufficient verifier list must return affirmative to allow the
+ * package to be considered verified. If there are zero sufficient verifiers,
+ * then package verification is considered complete.
+ */
+class PackageVerificationState {
+ private final InstallArgs mArgs;
+
+ private final SparseBooleanArray mSufficientVerifierUids;
+
+ private final int mRequiredVerifierUid;
+
+ private boolean mSufficientVerificationComplete;
+
+ private boolean mSufficientVerificationPassed;
+
+ private boolean mRequiredVerificationComplete;
+
+ private boolean mRequiredVerificationPassed;
+
+ /**
+ * Create a new package verification state where {@code requiredVerifierUid}
+ * is the user ID for the package that must reply affirmative before things
+ * can continue.
+ *
+ * @param requiredVerifierUid user ID of required package verifier
+ * @param args
+ */
+ public PackageVerificationState(int requiredVerifierUid, InstallArgs args) {
+ mRequiredVerifierUid = requiredVerifierUid;
+ mArgs = args;
+ mSufficientVerifierUids = new SparseBooleanArray();
+ }
+
+ public InstallArgs getInstallArgs() {
+ return mArgs;
+ }
+
+ /**
+ * Add a verifier which is added to our sufficient list.
+ *
+ * @param uid user ID of sufficient verifier
+ */
+ public void addSufficientVerifier(int uid) {
+ mSufficientVerifierUids.put(uid, true);
+ }
+
+ /**
+ * Should be called when a verification is received from an agent so the
+ * state of the package verification can be tracked.
+ *
+ * @param uid user ID of the verifying agent
+ * @return {@code true} if the verifying agent actually exists in our list
+ */
+ public boolean setVerifierResponse(int uid, int code) {
+ if (uid == mRequiredVerifierUid) {
+ mRequiredVerificationComplete = true;
+ switch (code) {
+ case PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT:
+ mSufficientVerifierUids.clear();
+ // fall through
+ case PackageManager.VERIFICATION_ALLOW:
+ mRequiredVerificationPassed = true;
+ break;
+ default:
+ mRequiredVerificationPassed = false;
+ }
+ return true;
+ } else {
+ if (mSufficientVerifierUids.get(uid)) {
+ if (code == PackageManager.VERIFICATION_ALLOW) {
+ mSufficientVerificationComplete = true;
+ mSufficientVerificationPassed = true;
+ }
+
+ mSufficientVerifierUids.delete(uid);
+ if (mSufficientVerifierUids.size() == 0) {
+ mSufficientVerificationComplete = true;
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns whether verification is considered complete. This means that the
+ * required verifier and at least one of the sufficient verifiers has
+ * returned a positive verification.
+ *
+ * @return {@code true} when verification is considered complete
+ */
+ public boolean isVerificationComplete() {
+ if (!mRequiredVerificationComplete) {
+ return false;
+ }
+
+ if (mSufficientVerifierUids.size() == 0) {
+ return true;
+ }
+
+ return mSufficientVerificationComplete;
+ }
+
+ /**
+ * Returns whether installation should be allowed. This should only be
+ * called after {@link #isVerificationComplete()} returns {@code true}.
+ *
+ * @return {@code true} if installation should be allowed
+ */
+ public boolean isInstallAllowed() {
+ if (!mRequiredVerificationPassed) {
+ return false;
+ }
+
+ if (mSufficientVerificationComplete) {
+ return mSufficientVerificationPassed;
+ }
+
+ return true;
+ }
+}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index a9ca304..3ea9e81 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -5193,6 +5193,7 @@
mRotation = rotation;
mAltOrientation = altOrientation;
+ mPolicy.setRotationLw(mRotation);
mWindowsFreezingScreen = true;
mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index 455d6649..3640a15 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -636,7 +636,7 @@
final boolean isHwAccelerated = (mAttrs.flags &
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : mAttrs.format;
- if (isHwAccelerated && mAttrs.format == PixelFormat.OPAQUE) {
+ if (!PixelFormat.formatHasAlpha(mAttrs.format)) {
flags |= Surface.OPAQUE;
}
mSurface = new Surface(
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index f85ce7f..49d2c55 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -578,7 +578,7 @@
uint32_t Layer::getTransformHint() const {
uint32_t orientation = 0;
if (!mFlinger->mDebugDisableTransformHint) {
- orientation = getOrientation();
+ orientation = getPlaneOrientation();
if (orientation & Transform::ROT_INVALID) {
orientation = 0;
}
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index e5ce814..7a47f62 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -45,6 +45,7 @@
mFlinger(flinger), mFiltering(false),
mNeedsFiltering(false), mInOverlay(false),
mOrientation(0),
+ mPlaneOrientation(0),
mTransactionFlags(0),
mPremultipliedAlpha(true), mName("unnamed"), mDebug(false),
mInvalidate(0)
@@ -256,6 +257,7 @@
// cache a few things...
mOrientation = tr.getOrientation();
+ mPlaneOrientation = planeTransform.getOrientation();
mTransform = tr;
mTransformedBounds = tr.makeBounds(w, h);
}
diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
index a14b397..268ba2d 100644
--- a/services/surfaceflinger/LayerBase.h
+++ b/services/surfaceflinger/LayerBase.h
@@ -221,6 +221,7 @@
inline State& currentState() { return mCurrentState; }
int32_t getOrientation() const { return mOrientation; }
+ int32_t getPlaneOrientation() const { return mPlaneOrientation; }
protected:
const GraphicPlane& graphicPlane(int dpy) const;
@@ -254,6 +255,7 @@
protected:
// cached during validateVisibility()
int32_t mOrientation;
+ int32_t mPlaneOrientation;
Transform mTransform;
GLfloat mVertices[4][2];
Rect mTransformedBounds;
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index 99ae027..2ead254 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -776,6 +776,7 @@
private void expectNetworkStatsPoll() throws Exception {
mNetManager.setGlobalAlert(anyLong());
expectLastCall().anyTimes();
+ expect(mConnManager.getTetheredIfacePairs()).andReturn(null).anyTimes();
}
private void assertStatsFilesExist(boolean exist) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java
new file mode 100644
index 0000000..ebd3633
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageVerificationStateTest.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2011 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 com.android.server.pm;
+
+import android.content.pm.PackageManager;
+import com.android.server.pm.PackageVerificationState;
+
+import android.test.AndroidTestCase;
+
+public class PackageVerificationStateTest extends AndroidTestCase {
+ private static final int REQUIRED_UID = 1948;
+
+ private static final int SUFFICIENT_UID_1 = 1005;
+
+ private static final int SUFFICIENT_UID_2 = 8938;
+
+ public void testPackageVerificationState_OnlyRequiredVerifier_AllowedInstall() {
+ PackageVerificationState state = new PackageVerificationState(REQUIRED_UID, null);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW);
+
+ assertTrue("Verification should be considered complete now",
+ state.isVerificationComplete());
+
+ assertTrue("Installation should be marked as allowed",
+ state.isInstallAllowed());
+ }
+
+ public void testPackageVerificationState_OnlyRequiredVerifier_DeniedInstall() {
+ PackageVerificationState state = new PackageVerificationState(REQUIRED_UID, null);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_REJECT);
+
+ assertTrue("Verification should be considered complete now",
+ state.isVerificationComplete());
+
+ assertFalse("Installation should be marked as allowed",
+ state.isInstallAllowed());
+ }
+
+ public void testPackageVerificationState_RequiredAndOneSufficient_RequiredDeniedInstall() {
+ PackageVerificationState state = new PackageVerificationState(REQUIRED_UID, null);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.addSufficientVerifier(SUFFICIENT_UID_1);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(SUFFICIENT_UID_1, PackageManager.VERIFICATION_ALLOW);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_REJECT);
+
+ assertTrue("Verification should be considered complete now",
+ state.isVerificationComplete());
+
+ assertFalse("Installation should be marked as allowed",
+ state.isInstallAllowed());
+ }
+
+ public void testPackageVerificationState_RequiredAndOneSufficient_SufficientDeniedInstall() {
+ PackageVerificationState state = new PackageVerificationState(REQUIRED_UID, null);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.addSufficientVerifier(SUFFICIENT_UID_1);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(SUFFICIENT_UID_1, PackageManager.VERIFICATION_REJECT);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW);
+
+ assertTrue("Verification should be considered complete now",
+ state.isVerificationComplete());
+
+ assertFalse("Installation should be marked as allowed",
+ state.isInstallAllowed());
+ }
+
+ public void testPackageVerificationState_RequiredAndTwoSufficient_OneSufficientIsEnough() {
+ PackageVerificationState state = new PackageVerificationState(REQUIRED_UID, null);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.addSufficientVerifier(SUFFICIENT_UID_1);
+ state.addSufficientVerifier(SUFFICIENT_UID_2);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(SUFFICIENT_UID_1, PackageManager.VERIFICATION_ALLOW);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW);
+
+ assertTrue("Verification should be considered complete now",
+ state.isVerificationComplete());
+
+ assertTrue("Installation should be marked as allowed",
+ state.isInstallAllowed());
+ }
+
+ public void testPackageVerificationState_RequiredAndTwoSufficient_SecondSufficientIsEnough() {
+ PackageVerificationState state = new PackageVerificationState(REQUIRED_UID, null);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.addSufficientVerifier(SUFFICIENT_UID_1);
+ state.addSufficientVerifier(SUFFICIENT_UID_2);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID, PackageManager.VERIFICATION_ALLOW);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(SUFFICIENT_UID_1, PackageManager.VERIFICATION_REJECT);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(SUFFICIENT_UID_2, PackageManager.VERIFICATION_ALLOW);
+
+ assertTrue("Verification should be considered complete now",
+ state.isVerificationComplete());
+
+ assertTrue("Installation should be marked as allowed",
+ state.isInstallAllowed());
+ }
+
+ public void testPackageVerificationState_RequiredAndTwoSufficient_RequiredOverrides() {
+ PackageVerificationState state = new PackageVerificationState(REQUIRED_UID, null);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.addSufficientVerifier(SUFFICIENT_UID_1);
+ state.addSufficientVerifier(SUFFICIENT_UID_2);
+
+ assertFalse("Verification should not be marked as complete yet",
+ state.isVerificationComplete());
+
+ state.setVerifierResponse(REQUIRED_UID,
+ PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
+
+ assertTrue("Verification should be marked as complete immediately",
+ state.isVerificationComplete());
+
+ assertTrue("Installation should be marked as allowed",
+ state.isInstallAllowed());
+
+ state.setVerifierResponse(SUFFICIENT_UID_1, PackageManager.VERIFICATION_REJECT);
+
+ assertTrue("Verification should still be marked as completed",
+ state.isVerificationComplete());
+
+ assertTrue("Installation should be marked as allowed still",
+ state.isInstallAllowed());
+
+ state.setVerifierResponse(SUFFICIENT_UID_2, PackageManager.VERIFICATION_ALLOW);
+
+ assertTrue("Verification should still be complete",
+ state.isVerificationComplete());
+
+ assertTrue("Installation should be marked as allowed still",
+ state.isInstallAllowed());
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
index 101dd55..abb4523 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
@@ -182,9 +182,4 @@
* in commercial configuration.
*/
static final String PROPERTY_TEST_CSIM = "persist.radio.test-csim";
-
- /**
- * Set to true to indicate a test ims registration required.
- */
- static final String PROPERTY_IMS_REG_REQUIRED = "persist.radio.imsregrequired";
}
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 3525abe..58680ea 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -36,9 +36,11 @@
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.Signature;
import android.content.pm.UserInfo;
import android.content.pm.ManifestDigest;
import android.content.pm.VerifierDeviceIdentity;
+import android.content.pm.VerifierInfo;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
diff --git a/tools/orientationplot/orientationplot.py b/tools/orientationplot/orientationplot.py
index 07449d4..3a44cb2 100755
--- a/tools/orientationplot/orientationplot.py
+++ b/tools/orientationplot/orientationplot.py
@@ -131,42 +131,28 @@
self.orientation_angle_axes, 'orientation', 'black')
self._add_timeseries_legend(self.orientation_angle_axes)
- self.actual_orientation = self._make_timeseries()
- self.proposed_orientation = self._make_timeseries()
+ self.current_rotation = self._make_timeseries()
+ self.proposed_rotation = self._make_timeseries()
+ self.proposal_rotation = self._make_timeseries()
self.orientation_axes = self._add_timeseries_axes(
- 5, 'Actual / Proposed Orientation and Confidence', 'rotation', [-1, 4],
+ 5, 'Current / Proposed Orientation and Confidence', 'rotation', [-1, 4],
sharex=shared_axis,
yticks=range(0, 4))
- self.actual_orientation_line = self._add_timeseries_line(
- self.orientation_axes, 'actual', 'black', linewidth=2)
- self.proposed_orientation_line = self._add_timeseries_line(
- self.orientation_axes, 'proposed', 'purple', linewidth=3)
+ self.current_rotation_line = self._add_timeseries_line(
+ self.orientation_axes, 'current', 'black', linewidth=2)
+ self.proposal_rotation_line = self._add_timeseries_line(
+ self.orientation_axes, 'proposal', 'purple', linewidth=3)
+ self.proposed_rotation_line = self._add_timeseries_line(
+ self.orientation_axes, 'proposed', 'green', linewidth=3)
self._add_timeseries_legend(self.orientation_axes)
- self.confidence = [[self._make_timeseries(), self._make_timeseries()] for i in range(0, 4)]
- self.confidence_polys = []
-
- self.combined_confidence = self._make_timeseries()
- self.orientation_confidence = self._make_timeseries()
- self.tilt_confidence = self._make_timeseries()
- self.magnitude_confidence = self._make_timeseries()
- self.confidence_axes = self._add_timeseries_axes(
- 6, 'Proposed Orientation Confidence Factors', 'confidence', [-0.1, 1.1],
- sharex=shared_axis,
- yticks=[0.0, 0.2, 0.4, 0.6, 0.8, 1.0])
- self.combined_confidence_line = self._add_timeseries_line(
- self.confidence_axes, 'combined', 'purple', linewidth=2)
- self.orientation_confidence_line = self._add_timeseries_line(
- self.confidence_axes, 'orientation', 'black')
- self.tilt_confidence_line = self._add_timeseries_line(
- self.confidence_axes, 'tilt', 'brown')
- self.magnitude_confidence_line = self._add_timeseries_line(
- self.confidence_axes, 'magnitude', 'orange')
- self._add_timeseries_legend(self.confidence_axes)
+ self.proposal_confidence = [[self._make_timeseries(), self._make_timeseries()]
+ for i in range(0, 4)]
+ self.proposal_confidence_polys = []
self.sample_latency = self._make_timeseries()
self.sample_latency_axes = self._add_timeseries_axes(
- 7, 'Accelerometer Sampling Latency', 'ms', [-10, 500],
+ 6, 'Accelerometer Sampling Latency', 'ms', [-10, 500],
sharex=shared_axis,
yticks=range(0, 500, 100))
self.sample_latency_line = self._add_timeseries_line(
@@ -186,7 +172,7 @@
# Add a subplot to the figure for a time series.
def _add_timeseries_axes(self, index, title, ylabel, ylim, yticks, sharex=None):
- num_graphs = 7
+ num_graphs = 6
height = 0.9 / num_graphs
top = 0.95 - height * index
axes = self.fig.add_axes([0.1, top, 0.8, height],
@@ -234,13 +220,10 @@
self.parse_magnitude = None
self.parse_tilt_angle = None
self.parse_orientation_angle = None
- self.parse_proposed_orientation = None
- self.parse_combined_confidence = None
- self.parse_orientation_confidence = None
- self.parse_tilt_confidence = None
- self.parse_magnitude_confidence = None
- self.parse_actual_orientation = None
- self.parse_confidence = None
+ self.parse_current_rotation = None
+ self.parse_proposed_rotation = None
+ self.parse_proposal_rotation = None
+ self.parse_proposal_confidence = None
self.parse_sample_latency = None
# Update samples.
@@ -284,26 +267,13 @@
if line.find('orientationAngle=') != -1:
self.parse_orientation_angle = self._get_following_number(line, 'orientationAngle=')
- if line.find('Proposal:') != -1:
- self.parse_proposed_orientation = self._get_following_number(line, 'proposedOrientation=')
- self.parse_combined_confidence = self._get_following_number(line, 'combinedConfidence=')
- self.parse_orientation_confidence = self._get_following_number(line, 'orientationConfidence=')
- self.parse_tilt_confidence = self._get_following_number(line, 'tiltConfidence=')
- self.parse_magnitude_confidence = self._get_following_number(line, 'magnitudeConfidence=')
-
if line.find('Result:') != -1:
- self.parse_actual_orientation = self._get_following_number(line, 'rotation=')
- self.parse_confidence = self._get_following_array_of_numbers(line, 'confidence=')
+ self.parse_current_rotation = self._get_following_number(line, 'currentRotation=')
+ self.parse_proposed_rotation = self._get_following_number(line, 'proposedRotation=')
+ self.parse_proposal_rotation = self._get_following_number(line, 'proposalRotation=')
+ self.parse_proposal_confidence = self._get_following_number(line, 'proposalConfidence=')
self.parse_sample_latency = self._get_following_number(line, 'timeDeltaMS=')
- for i in range(0, 4):
- if self.parse_confidence is not None:
- self._append(self.confidence[i][0], timeindex, i)
- self._append(self.confidence[i][1], timeindex, i + self.parse_confidence[i])
- else:
- self._append(self.confidence[i][0], timeindex, None)
- self._append(self.confidence[i][1], timeindex, None)
-
self._append(self.raw_acceleration_x, timeindex, self.parse_raw_acceleration_x)
self._append(self.raw_acceleration_y, timeindex, self.parse_raw_acceleration_y)
self._append(self.raw_acceleration_z, timeindex, self.parse_raw_acceleration_z)
@@ -313,12 +283,22 @@
self._append(self.magnitude, timeindex, self.parse_magnitude)
self._append(self.tilt_angle, timeindex, self.parse_tilt_angle)
self._append(self.orientation_angle, timeindex, self.parse_orientation_angle)
- self._append(self.actual_orientation, timeindex, self.parse_actual_orientation)
- self._append(self.proposed_orientation, timeindex, self.parse_proposed_orientation)
- self._append(self.combined_confidence, timeindex, self.parse_combined_confidence)
- self._append(self.orientation_confidence, timeindex, self.parse_orientation_confidence)
- self._append(self.tilt_confidence, timeindex, self.parse_tilt_confidence)
- self._append(self.magnitude_confidence, timeindex, self.parse_magnitude_confidence)
+ self._append(self.current_rotation, timeindex, self.parse_current_rotation)
+ if self.parse_proposed_rotation >= 0:
+ self._append(self.proposed_rotation, timeindex, self.parse_proposed_rotation)
+ else:
+ self._append(self.proposed_rotation, timeindex, None)
+ if self.parse_proposal_rotation >= 0:
+ self._append(self.proposal_rotation, timeindex, self.parse_proposal_rotation)
+ else:
+ self._append(self.proposal_rotation, timeindex, None)
+ for i in range(0, 4):
+ self._append(self.proposal_confidence[i][0], timeindex, i)
+ if i == self.parse_proposal_rotation:
+ self._append(self.proposal_confidence[i][1], timeindex,
+ i + self.parse_proposal_confidence)
+ else:
+ self._append(self.proposal_confidence[i][1], timeindex, i)
self._append(self.sample_latency, timeindex, self.parse_sample_latency)
self._reset_parse_state()
@@ -335,16 +315,13 @@
self._scroll(self.magnitude, bottom)
self._scroll(self.tilt_angle, bottom)
self._scroll(self.orientation_angle, bottom)
- self._scroll(self.actual_orientation, bottom)
- self._scroll(self.proposed_orientation, bottom)
- self._scroll(self.combined_confidence, bottom)
- self._scroll(self.orientation_confidence, bottom)
- self._scroll(self.tilt_confidence, bottom)
- self._scroll(self.magnitude_confidence, bottom)
- self._scroll(self.sample_latency, bottom)
+ self._scroll(self.current_rotation, bottom)
+ self._scroll(self.proposed_rotation, bottom)
+ self._scroll(self.proposal_rotation, bottom)
for i in range(0, 4):
- self._scroll(self.confidence[i][0], bottom)
- self._scroll(self.confidence[i][1], bottom)
+ self._scroll(self.proposal_confidence[i][0], bottom)
+ self._scroll(self.proposal_confidence[i][1], bottom)
+ self._scroll(self.sample_latency, bottom)
# Redraw the plots.
self.raw_acceleration_line_x.set_data(self.raw_acceleration_x)
@@ -356,20 +333,19 @@
self.magnitude_line.set_data(self.magnitude)
self.tilt_angle_line.set_data(self.tilt_angle)
self.orientation_angle_line.set_data(self.orientation_angle)
- self.actual_orientation_line.set_data(self.actual_orientation)
- self.proposed_orientation_line.set_data(self.proposed_orientation)
- self.combined_confidence_line.set_data(self.combined_confidence)
- self.orientation_confidence_line.set_data(self.orientation_confidence)
- self.tilt_confidence_line.set_data(self.tilt_confidence)
- self.magnitude_confidence_line.set_data(self.magnitude_confidence)
+ self.current_rotation_line.set_data(self.current_rotation)
+ self.proposed_rotation_line.set_data(self.proposed_rotation)
+ self.proposal_rotation_line.set_data(self.proposal_rotation)
self.sample_latency_line.set_data(self.sample_latency)
- for poly in self.confidence_polys:
+ for poly in self.proposal_confidence_polys:
poly.remove()
- self.confidence_polys = []
+ self.proposal_confidence_polys = []
for i in range(0, 4):
- self.confidence_polys.append(self.orientation_axes.fill_between(self.confidence[i][0][0],
- self.confidence[i][0][1], self.confidence[i][1][1],
+ self.proposal_confidence_polys.append(self.orientation_axes.fill_between(
+ self.proposal_confidence[i][0][0],
+ self.proposal_confidence[i][0][1],
+ self.proposal_confidence[i][1][1],
facecolor='goldenrod', edgecolor='goldenrod'))
self.fig.canvas.draw_idle()