summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/java/android/accounts/AccountManagerService.java30
-rw-r--r--core/java/android/app/ApplicationPackageManager.java17
-rw-r--r--core/java/android/content/Intent.java45
-rw-r--r--core/java/android/content/SyncManager.java23
-rw-r--r--core/java/android/content/SyncQueue.java13
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl2
-rw-r--r--core/java/android/content/pm/PackageManager.java18
-rw-r--r--core/java/android/content/pm/PackageParser.java97
-rw-r--r--core/java/android/database/sqlite/SQLiteSession.java8
-rw-r--r--core/java/android/net/SSLCertificateSocketFactory.java64
-rw-r--r--core/java/android/view/DisplayList.java24
-rw-r--r--core/java/android/view/FocusFinder.java82
-rw-r--r--core/java/android/view/GLES20Canvas.java8
-rw-r--r--core/java/android/view/HardwareCanvas.java16
-rw-r--r--core/java/android/view/HardwareRenderer.java10
-rw-r--r--core/java/android/view/View.java81
-rw-r--r--core/java/android/view/ViewGroup.java15
-rw-r--r--core/java/android/webkit/DebugFlags.java7
-rw-r--r--core/java/android/webkit/FindListener.java32
-rw-r--r--core/java/android/webkit/WebView.java24
-rw-r--r--core/java/android/webkit/WebViewClassic.java33
-rw-r--r--core/java/android/webkit/WebViewCore.java1
-rw-r--r--core/java/android/webkit/WebViewProvider.java2
-rw-r--r--core/java/android/widget/AdapterViewFlipper.java26
-rw-r--r--core/java/android/widget/ImageView.java133
-rw-r--r--core/java/android/widget/RadioGroup.java2
-rw-r--r--core/java/android/widget/RatingBar.java4
-rw-r--r--core/java/android/widget/TextView.java6
-rw-r--r--core/java/com/android/internal/util/StateMachine.java61
-rw-r--r--core/java/com/google/android/mms/pdu/PduPersister.java124
-rw-r--r--core/java/com/google/android/mms/util/DownloadDrmHelper.java111
-rw-r--r--core/java/com/google/android/mms/util/DrmConvertSession.java172
-rw-r--r--core/jni/android_view_GLES20Canvas.cpp22
-rw-r--r--core/res/AndroidManifest.xml21
-rw-r--r--core/res/res/values-am/strings.xml4
-rw-r--r--core/res/res/values-in/strings.xml6
-rw-r--r--core/res/res/values-zh-rTW/strings.xml3
-rw-r--r--core/tests/coretests/src/android/net/SSLTest.java40
38 files changed, 1122 insertions, 265 deletions
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index 27c9c8b..197c1bd 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -231,6 +231,14 @@ public class AccountManagerService
}
}, intentFilter);
+ IntentFilter userFilter = new IntentFilter();
+ userFilter.addAction(Intent.ACTION_USER_REMOVED);
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onUserRemoved(intent);
+ }
+ }, userFilter);
}
private UserAccounts initUser(int userId) {
@@ -347,6 +355,28 @@ public class AccountManagerService
}
}
+ private void onUserRemoved(Intent intent) {
+ int userId = intent.getIntExtra(Intent.EXTRA_USERID, -1);
+ if (userId < 1) return;
+
+ UserAccounts accounts;
+ synchronized (mUsers) {
+ accounts = mUsers.get(userId);
+ mUsers.remove(userId);
+ }
+ if (accounts == null) {
+ File dbFile = new File(getDatabaseName(userId));
+ dbFile.delete();
+ return;
+ }
+
+ synchronized (accounts.cacheLock) {
+ accounts.openHelper.close();
+ File dbFile = new File(getDatabaseName(userId));
+ dbFile.delete();
+ }
+ }
+
private List<UserInfo> getAllUsers() {
try {
return AppGlobals.getPackageManager().getUsers();
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index f38540c..0510de1 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1215,6 +1215,18 @@ final class ApplicationPackageManager extends PackageManager {
* @hide
*/
@Override
+ public UserInfo getUser(int userId) {
+ try {
+ return mPM.getUser(userId);
+ } catch (RemoteException re) {
+ return null;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
public boolean removeUser(int id) {
try {
return mPM.removeUser(id);
@@ -1228,7 +1240,10 @@ final class ApplicationPackageManager extends PackageManager {
*/
@Override
public void updateUserName(int id, String name) {
- // TODO:
+ try {
+ mPM.updateUserName(id, name);
+ } catch (RemoteException re) {
+ }
}
/**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 1c9ef38..2a9f1af 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2141,6 +2141,30 @@ public class Intent implements Parcelable, Cloneable {
public static final String ACTION_PRE_BOOT_COMPLETED =
"android.intent.action.PRE_BOOT_COMPLETED";
+ /**
+ * Broadcast sent to the system when a user is added. Carries an extra EXTRA_USERID that has the
+ * userid of the new user.
+ * @hide
+ */
+ public static final String ACTION_USER_ADDED =
+ "android.intent.action.USER_ADDED";
+
+ /**
+ * Broadcast sent to the system when a user is removed. Carries an extra EXTRA_USERID that has
+ * the userid of the user.
+ * @hide
+ */
+ public static final String ACTION_USER_REMOVED =
+ "android.intent.action.USER_REMOVED";
+
+ /**
+ * Broadcast sent to the system when the user switches. Carries an extra EXTRA_USERID that has
+ * the userid of the user to become the current one.
+ * @hide
+ */
+ public static final String ACTION_USER_SWITCHED =
+ "android.intent.action.USER_SWITCHED";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Standard intent categories (see addCategory()).
@@ -2682,6 +2706,13 @@ public class Intent implements Parcelable, Cloneable {
public static final String EXTRA_LOCAL_ONLY =
"android.intent.extra.LOCAL_ONLY";
+ /**
+ * The userid carried with broadcast intents related to addition, removal and switching of users
+ * - {@link #ACTION_USER_ADDED}, {@link #ACTION_USER_REMOVED} and {@link #ACTION_USER_SWITCHED}.
+ * @hide
+ */
+ public static final String EXTRA_USERID =
+ "android.intent.extra.user_id";
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Intent flags (see mFlags variable).
@@ -6483,7 +6514,12 @@ public class Intent implements Parcelable, Cloneable {
final String action = getAction();
if (ACTION_SEND.equals(action)) {
- final Uri stream = getParcelableExtra(EXTRA_STREAM);
+ final Uri stream;
+ try {
+ stream = getParcelableExtra(EXTRA_STREAM);
+ } catch (ClassCastException e) {
+ return;
+ }
if (stream != null) {
final ClipData clipData = new ClipData(
null, new String[] { getType() }, new ClipData.Item(stream));
@@ -6493,7 +6529,12 @@ public class Intent implements Parcelable, Cloneable {
}
} else if (ACTION_SEND_MULTIPLE.equals(action)) {
- final ArrayList<Uri> streams = getParcelableArrayListExtra(EXTRA_STREAM);
+ final ArrayList<Uri> streams;
+ try {
+ streams = getParcelableArrayListExtra(EXTRA_STREAM);
+ } catch (ClassCastException e) {
+ return;
+ }
if (streams != null && streams.size() > 0) {
final Uri firstStream = streams.get(0);
final ClipData clipData = new ClipData(
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index b7dfe92..06dfe90 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -326,6 +326,13 @@ public class SyncManager implements OnAccountsUpdateListener {
}
};
+ private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onUserRemoved(intent);
+ }
+ };
+
private static final String ACTION_SYNC_ALARM = "android.content.syncmanager.SYNC_ALARM";
private final SyncHandler mSyncHandler;
@@ -420,6 +427,10 @@ public class SyncManager implements OnAccountsUpdateListener {
intentFilter.setPriority(100);
context.registerReceiver(mShutdownIntentReceiver, intentFilter);
+ intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_USER_REMOVED);
+ mContext.registerReceiver(mUserIntentReceiver, intentFilter);
+
if (!factoryTest) {
mNotificationMgr = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -905,6 +916,18 @@ public class SyncManager implements OnAccountsUpdateListener {
}
}
+ private void onUserRemoved(Intent intent) {
+ int userId = intent.getIntExtra(Intent.EXTRA_USERID, -1);
+ if (userId == -1) return;
+
+ // Clean up the storage engine database
+ mSyncStorageEngine.doDatabaseCleanup(new Account[0], userId);
+ onAccountsUpdated(null);
+ synchronized (mSyncQueue) {
+ mSyncQueue.removeUser(userId);
+ }
+ }
+
/**
* @hide
*/
diff --git a/core/java/android/content/SyncQueue.java b/core/java/android/content/SyncQueue.java
index 06da6fa..c18c86b 100644
--- a/core/java/android/content/SyncQueue.java
+++ b/core/java/android/content/SyncQueue.java
@@ -117,6 +117,19 @@ public class SyncQueue {
return true;
}
+ public void removeUser(int userId) {
+ ArrayList<SyncOperation> opsToRemove = new ArrayList<SyncOperation>();
+ for (SyncOperation op : mOperationsMap.values()) {
+ if (op.userId == userId) {
+ opsToRemove.add(op);
+ }
+ }
+
+ for (SyncOperation op : opsToRemove) {
+ remove(op);
+ }
+ }
+
/**
* Remove the specified operation if it is in the queue.
* @param operation the operation to remove
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index d89d2de..56fd5f8 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -358,6 +358,7 @@ interface IPackageManager {
UserInfo createUser(in String name, int flags);
boolean removeUser(int userId);
+ void updateUserName(int userId, String name);
void installPackageWithVerification(in Uri packageURI, in IPackageInstallObserver observer,
int flags, in String installerPackageName, in Uri verificationURI,
@@ -370,6 +371,7 @@ interface IPackageManager {
boolean isFirstBoot();
List<UserInfo> getUsers();
+ UserInfo getUser(int userId);
void setPermissionEnforcement(String permission, int enforcement);
int getPermissionEnforcement(String permission);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 55426b8..b06b4a5 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2153,7 +2153,8 @@ public abstract class PackageManager {
if ((flags & GET_SIGNATURES) != 0) {
packageParser.collectCertificates(pkg, 0);
}
- return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null);
+ return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, false,
+ COMPONENT_ENABLED_STATE_DEFAULT);
}
/**
@@ -2637,10 +2638,17 @@ public abstract class PackageManager {
public abstract void updateUserFlags(int id, int flags);
/**
- * Returns the device identity that verifiers can use to associate their
- * scheme to a particular device. This should not be used by anything other
- * than a package verifier.
- *
+ * Returns the details for the user specified by userId.
+ * @param userId the user id of the user
+ * @return UserInfo for the specified user, or null if no such user exists.
+ * @hide
+ */
+ public abstract UserInfo getUser(int userId);
+
+ /**
+ * Returns the device identity that verifiers can use to associate their scheme to a particular
+ * device. This should not be used by anything other than a package verifier.
+ *
* @return identity that uniquely identifies current device
* @hide
*/
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 07d231a..eb8536f 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -230,6 +230,15 @@ public class PackageParser {
return name.endsWith(".apk");
}
+ public static PackageInfo generatePackageInfo(PackageParser.Package p,
+ int gids[], int flags, long firstInstallTime, long lastUpdateTime,
+ HashSet<String> grantedPermissions) {
+
+ return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
+ grantedPermissions, false, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
+ UserId.getCallingUserId());
+ }
+
/**
* Generate and return the {@link PackageInfo} for a parsed package.
*
@@ -238,15 +247,15 @@ public class PackageParser {
*/
public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
- HashSet<String> grantedPermissions) {
+ HashSet<String> grantedPermissions, boolean stopped, int enabledState) {
return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
- grantedPermissions, UserId.getCallingUserId());
+ grantedPermissions, stopped, enabledState, UserId.getCallingUserId());
}
- static PackageInfo generatePackageInfo(PackageParser.Package p,
+ public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
- HashSet<String> grantedPermissions, int userId) {
+ HashSet<String> grantedPermissions, boolean stopped, int enabledState, int userId) {
PackageInfo pi = new PackageInfo();
pi.packageName = p.packageName;
@@ -254,7 +263,7 @@ public class PackageParser {
pi.versionName = p.mVersionName;
pi.sharedUserId = p.mSharedUserId;
pi.sharedUserLabel = p.mSharedUserLabel;
- pi.applicationInfo = generateApplicationInfo(p, flags);
+ pi.applicationInfo = generateApplicationInfo(p, flags, stopped, enabledState, userId);
pi.installLocation = p.installLocation;
pi.firstInstallTime = firstInstallTime;
pi.lastUpdateTime = lastUpdateTime;
@@ -290,7 +299,7 @@ public class PackageParser {
if (activity.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
pi.activities[j++] = generateActivityInfo(p.activities.get(i), flags,
- userId);
+ stopped, enabledState, userId);
}
}
}
@@ -311,7 +320,8 @@ public class PackageParser {
final Activity activity = p.receivers.get(i);
if (activity.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- pi.receivers[j++] = generateActivityInfo(p.receivers.get(i), flags, userId);
+ pi.receivers[j++] = generateActivityInfo(p.receivers.get(i), flags,
+ stopped, enabledState, userId);
}
}
}
@@ -332,7 +342,8 @@ public class PackageParser {
final Service service = p.services.get(i);
if (service.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- pi.services[j++] = generateServiceInfo(p.services.get(i), flags, userId);
+ pi.services[j++] = generateServiceInfo(p.services.get(i), flags, stopped,
+ enabledState, userId);
}
}
}
@@ -353,7 +364,8 @@ public class PackageParser {
final Provider provider = p.providers.get(i);
if (provider.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- pi.providers[j++] = generateProviderInfo(p.providers.get(i), flags, userId);
+ pi.providers[j++] = generateProviderInfo(p.providers.get(i), flags, stopped,
+ enabledState, userId);
}
}
}
@@ -3068,11 +3080,11 @@ public class PackageParser {
// For use by package manager to keep track of where it has done dexopt.
public boolean mDidDexOpt;
- // User set enabled state.
- public int mSetEnabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
-
- // Whether the package has been stopped.
- public boolean mSetStopped = false;
+ // // User set enabled state.
+ // public int mSetEnabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+ //
+ // // Whether the package has been stopped.
+ // public boolean mSetStopped = false;
// Additional data supplied by callers.
public Object mExtras;
@@ -3337,9 +3349,9 @@ public class PackageParser {
}
}
- private static boolean copyNeeded(int flags, Package p, Bundle metaData) {
- if (p.mSetEnabled != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
- boolean enabled = p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+ private static boolean copyNeeded(int flags, Package p, int enabledState, Bundle metaData) {
+ if (enabledState != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+ boolean enabled = enabledState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
if (p.applicationInfo.enabled != enabled) {
return true;
}
@@ -3355,30 +3367,32 @@ public class PackageParser {
return false;
}
- public static ApplicationInfo generateApplicationInfo(Package p, int flags) {
- return generateApplicationInfo(p, flags, UserId.getCallingUserId());
+ public static ApplicationInfo generateApplicationInfo(Package p, int flags, boolean stopped,
+ int enabledState) {
+ return generateApplicationInfo(p, flags, stopped, enabledState, UserId.getCallingUserId());
}
- public static ApplicationInfo generateApplicationInfo(Package p, int flags, int userId) {
+ public static ApplicationInfo generateApplicationInfo(Package p, int flags,
+ boolean stopped, int enabledState, int userId) {
if (p == null) return null;
- if (!copyNeeded(flags, p, null) && userId == 0) {
+ if (!copyNeeded(flags, p, enabledState, null) && userId == 0) {
// CompatibilityMode is global state. It's safe to modify the instance
// of the package.
if (!sCompatibilityModeEnabled) {
p.applicationInfo.disableCompatibilityMode();
}
- if (p.mSetStopped) {
+ if (stopped) {
p.applicationInfo.flags |= ApplicationInfo.FLAG_STOPPED;
} else {
p.applicationInfo.flags &= ~ApplicationInfo.FLAG_STOPPED;
}
- if (p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+ if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
p.applicationInfo.enabled = true;
- } else if (p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
- || p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+ } else if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+ || enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
p.applicationInfo.enabled = false;
}
- p.applicationInfo.enabledSetting = p.mSetEnabled;
+ p.applicationInfo.enabledSetting = enabledState;
return p.applicationInfo;
}
@@ -3397,18 +3411,18 @@ public class PackageParser {
if (!sCompatibilityModeEnabled) {
ai.disableCompatibilityMode();
}
- if (p.mSetStopped) {
+ if (stopped) {
p.applicationInfo.flags |= ApplicationInfo.FLAG_STOPPED;
} else {
p.applicationInfo.flags &= ~ApplicationInfo.FLAG_STOPPED;
}
- if (p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+ if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
ai.enabled = true;
- } else if (p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
- || p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+ } else if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+ || enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
ai.enabled = false;
}
- ai.enabledSetting = p.mSetEnabled;
+ ai.enabledSetting = enabledState;
return ai;
}
@@ -3455,15 +3469,16 @@ public class PackageParser {
}
}
- public static final ActivityInfo generateActivityInfo(Activity a, int flags, int userId) {
+ public static final ActivityInfo generateActivityInfo(Activity a, int flags, boolean stopped,
+ int enabledState, int userId) {
if (a == null) return null;
- if (!copyNeeded(flags, a.owner, a.metaData) && userId == 0) {
+ if (!copyNeeded(flags, a.owner, enabledState, a.metaData) && userId == 0) {
return a.info;
}
// Make shallow copies so we can store the metadata safely
ActivityInfo ai = new ActivityInfo(a.info);
ai.metaData = a.metaData;
- ai.applicationInfo = generateApplicationInfo(a.owner, flags, userId);
+ ai.applicationInfo = generateApplicationInfo(a.owner, flags, stopped, enabledState, userId);
return ai;
}
@@ -3488,16 +3503,17 @@ public class PackageParser {
}
}
- public static final ServiceInfo generateServiceInfo(Service s, int flags, int userId) {
+ public static final ServiceInfo generateServiceInfo(Service s, int flags, boolean stopped,
+ int enabledState, int userId) {
if (s == null) return null;
- if (!copyNeeded(flags, s.owner, s.metaData)
+ if (!copyNeeded(flags, s.owner, enabledState, s.metaData)
&& userId == UserId.getUserId(s.info.applicationInfo.uid)) {
return s.info;
}
// Make shallow copies so we can store the metadata safely
ServiceInfo si = new ServiceInfo(s.info);
si.metaData = s.metaData;
- si.applicationInfo = generateApplicationInfo(s.owner, flags, userId);
+ si.applicationInfo = generateApplicationInfo(s.owner, flags, stopped, enabledState, userId);
return si;
}
@@ -3530,9 +3546,10 @@ public class PackageParser {
}
}
- public static final ProviderInfo generateProviderInfo(Provider p, int flags, int userId) {
+ public static final ProviderInfo generateProviderInfo(Provider p, int flags, boolean stopped,
+ int enabledState, int userId) {
if (p == null) return null;
- if (!copyNeeded(flags, p.owner, p.metaData)
+ if (!copyNeeded(flags, p.owner, enabledState, p.metaData)
&& ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) != 0
|| p.info.uriPermissionPatterns == null)
&& userId == 0) {
@@ -3544,7 +3561,7 @@ public class PackageParser {
if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) {
pi.uriPermissionPatterns = null;
}
- pi.applicationInfo = generateApplicationInfo(p.owner, flags, userId);
+ pi.applicationInfo = generateApplicationInfo(p.owner, flags, stopped, enabledState, userId);
return pi;
}
diff --git a/core/java/android/database/sqlite/SQLiteSession.java b/core/java/android/database/sqlite/SQLiteSession.java
index 43efb03..9410243 100644
--- a/core/java/android/database/sqlite/SQLiteSession.java
+++ b/core/java/android/database/sqlite/SQLiteSession.java
@@ -398,16 +398,16 @@ public final class SQLiteSession {
throwIfNoTransaction();
assert mConnection != null;
- endTransactionUnchecked(cancellationSignal);
+ endTransactionUnchecked(cancellationSignal, false);
}
- private void endTransactionUnchecked(CancellationSignal cancellationSignal) {
+ private void endTransactionUnchecked(CancellationSignal cancellationSignal, boolean yielding) {
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
}
final Transaction top = mTransactionStack;
- boolean successful = top.mMarkedSuccessful && !top.mChildFailed;
+ boolean successful = (top.mMarkedSuccessful || yielding) && !top.mChildFailed;
RuntimeException listenerException = null;
final SQLiteTransactionListener listener = top.mListener;
@@ -534,7 +534,7 @@ public final class SQLiteSession {
final int transactionMode = mTransactionStack.mMode;
final SQLiteTransactionListener listener = mTransactionStack.mListener;
final int connectionFlags = mConnectionFlags;
- endTransactionUnchecked(cancellationSignal); // might throw
+ endTransactionUnchecked(cancellationSignal, true); // might throw
if (sleepAfterYieldDelayMillis > 0) {
try {
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index 5c4b258..6a4f1f2 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -18,13 +18,11 @@ package android.net;
import android.os.SystemProperties;
import android.util.Log;
-
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.security.KeyManagementException;
import java.security.cert.X509Certificate;
-
import javax.net.SocketFactory;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
@@ -36,7 +34,6 @@ import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
-
import org.apache.harmony.xnet.provider.jsse.OpenSSLContextImpl;
import org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl;
import org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache;
@@ -89,6 +86,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
private SSLSocketFactory mSecureFactory = null;
private TrustManager[] mTrustManagers = null;
private KeyManager[] mKeyManagers = null;
+ private byte[] mNpnProtocols = null;
private final int mHandshakeTimeoutMillis;
private final SSLClientSessionCache mSessionCache;
@@ -251,6 +249,60 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
}
/**
+ * Sets the <a href="http://technotes.googlecode.com/git/nextprotoneg.html">Next
+ * Protocol Negotiation (NPN)</a> protocols that this peer is interested in.
+ *
+ * <p>For servers this is the sequence of protocols to advertise as
+ * supported, in order of preference. This list is sent unencrypted to
+ * all clients that support NPN.
+ *
+ * <p>For clients this is a list of supported protocols to match against the
+ * server's list. If there is no protocol supported by both client and
+ * server then the first protocol in the client's list will be selected.
+ * The order of the client's protocols is otherwise insignificant.
+ *
+ * @param npnProtocols a possibly-empty list of protocol byte arrays. All
+ * arrays must be non-empty and of length less than 256.
+ */
+ public void setNpnProtocols(byte[][] npnProtocols) {
+ this.mNpnProtocols = toNpnProtocolsList(npnProtocols);
+ }
+
+ /**
+ * Returns an array containing the concatenation of length-prefixed byte
+ * strings.
+ */
+ static byte[] toNpnProtocolsList(byte[]... npnProtocols) {
+ int totalLength = 0;
+ for (byte[] s : npnProtocols) {
+ if (s.length == 0 || s.length > 255) {
+ throw new IllegalArgumentException("s.length == 0 || s.length > 255: " + s.length);
+ }
+ totalLength += 1 + s.length;
+ }
+ byte[] result = new byte[totalLength];
+ int pos = 0;
+ for (byte[] s : npnProtocols) {
+ result[pos++] = (byte) s.length;
+ for (byte b : s) {
+ result[pos++] = b;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns the <a href="http://technotes.googlecode.com/git/nextprotoneg.html">Next
+ * Protocol Negotiation (NPN)</a> protocol selected by client and server, or
+ * null if no protocol was negotiated.
+ *
+ * @param socket a socket created by this factory.
+ */
+ public byte[] getNpnSelectedProtocol(Socket socket) {
+ return ((OpenSSLSocketImpl) socket).getNpnSelectedProtocol();
+ }
+
+ /**
* Sets the {@link KeyManager}s to be used for connections made by this factory.
*/
public void setKeyManagers(KeyManager[] keyManagers) {
@@ -271,6 +323,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
@Override
public Socket createSocket(Socket k, String host, int port, boolean close) throws IOException {
OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(k, host, port, close);
+ s.setNpnProtocols(mNpnProtocols);
s.setHandshakeTimeout(mHandshakeTimeoutMillis);
if (mSecure) {
verifyHostname(s, host);
@@ -289,6 +342,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
@Override
public Socket createSocket() throws IOException {
OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket();
+ s.setNpnProtocols(mNpnProtocols);
s.setHandshakeTimeout(mHandshakeTimeoutMillis);
return s;
}
@@ -305,6 +359,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
throws IOException {
OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(
addr, port, localAddr, localPort);
+ s.setNpnProtocols(mNpnProtocols);
s.setHandshakeTimeout(mHandshakeTimeoutMillis);
return s;
}
@@ -319,6 +374,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
@Override
public Socket createSocket(InetAddress addr, int port) throws IOException {
OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(addr, port);
+ s.setNpnProtocols(mNpnProtocols);
s.setHandshakeTimeout(mHandshakeTimeoutMillis);
return s;
}
@@ -334,6 +390,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
throws IOException {
OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(
host, port, localAddr, localPort);
+ s.setNpnProtocols(mNpnProtocols);
s.setHandshakeTimeout(mHandshakeTimeoutMillis);
if (mSecure) {
verifyHostname(s, host);
@@ -350,6 +407,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
@Override
public Socket createSocket(String host, int port) throws IOException {
OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(host, port);
+ s.setNpnProtocols(mNpnProtocols);
s.setHandshakeTimeout(mHandshakeTimeoutMillis);
if (mSecure) {
verifyHostname(s, host);
diff --git a/core/java/android/view/DisplayList.java b/core/java/android/view/DisplayList.java
index a50f09f..e2aafa9 100644
--- a/core/java/android/view/DisplayList.java
+++ b/core/java/android/view/DisplayList.java
@@ -35,6 +35,30 @@ public abstract class DisplayList {
*/
public static final int FLAG_CLIP_CHILDREN = 0x1;
+ // NOTE: The STATUS_* values *must* match the enum in DrawGlInfo.h
+
+ /**
+ * Indicates that the display list is done drawing.
+ *
+ * @see HardwareCanvas#drawDisplayList(DisplayList, int, int, android.graphics.Rect, int)
+ */
+ public static final int STATUS_DONE = 0x0;
+
+ /**
+ * Indicates that the display list needs another drawing pass.
+ *
+ * @see HardwareCanvas#drawDisplayList(DisplayList, int, int, android.graphics.Rect, int)
+ */
+ public static final int STATUS_DRAW = 0x1;
+
+ /**
+ * Indicates that the display list needs to re-execute its GL functors.
+ *
+ * @see HardwareCanvas#drawDisplayList(DisplayList, int, int, android.graphics.Rect, int)
+ * @see HardwareCanvas#callDrawGLFunction(int)
+ */
+ public static final int STATUS_INVOKE = 0x2;
+
/**
* Starts recording the display list. All operations performed on the
* returned canvas are recorded and stored in this display list.
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index d9bf918..9639faf 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -79,25 +79,45 @@ public class FocusFinder {
switch (direction) {
case View.FOCUS_RIGHT:
case View.FOCUS_DOWN:
+ setFocusBottomRight(root);
+ break;
case View.FOCUS_FORWARD:
- final int rootTop = root.getScrollY();
- final int rootLeft = root.getScrollX();
- mFocusedRect.set(rootLeft, rootTop, rootLeft, rootTop);
+ if (focused != null && focused.isLayoutRtl()) {
+ setFocusTopLeft(root);
+ } else {
+ setFocusBottomRight(root);
+ }
break;
case View.FOCUS_LEFT:
case View.FOCUS_UP:
+ setFocusTopLeft(root);
+ break;
case View.FOCUS_BACKWARD:
- final int rootBottom = root.getScrollY() + root.getHeight();
- final int rootRight = root.getScrollX() + root.getWidth();
- mFocusedRect.set(rootRight, rootBottom,
- rootRight, rootBottom);
+ if (focused != null && focused.isLayoutRtl()) {
+ setFocusBottomRight(root);
+ } else {
+ setFocusTopLeft(root);
break;
+ }
}
}
return findNextFocus(root, focused, mFocusedRect, direction);
}
+ private void setFocusTopLeft(ViewGroup root) {
+ final int rootBottom = root.getScrollY() + root.getHeight();
+ final int rootRight = root.getScrollX() + root.getWidth();
+ mFocusedRect.set(rootRight, rootBottom,
+ rootRight, rootBottom);
+ }
+
+ private void setFocusBottomRight(ViewGroup root) {
+ final int rootTop = root.getScrollY();
+ final int rootLeft = root.getScrollX();
+ mFocusedRect.set(rootLeft, rootTop, rootLeft, rootTop);
+ }
+
/**
* Find the next view to take focus in root's descendants, searching from
* a particular rectangle in root's coordinates.
@@ -135,22 +155,10 @@ public class FocusFinder {
final int count = focusables.size();
switch (direction) {
case View.FOCUS_FORWARD:
- if (focused != null) {
- int position = focusables.lastIndexOf(focused);
- if (position >= 0 && position + 1 < count) {
- return focusables.get(position + 1);
- }
- }
- return focusables.get(0);
+ return getForwardFocusable(focused, focusables, count);
case View.FOCUS_BACKWARD:
- if (focused != null) {
- int position = focusables.indexOf(focused);
- if (position > 0) {
- return focusables.get(position - 1);
- }
- }
- return focusables.get(count - 1);
+ return getBackwardFocusable(focused, focusables, count);
}
return null;
}
@@ -193,6 +201,38 @@ public class FocusFinder {
return closest;
}
+ private View getForwardFocusable(View focused, ArrayList<View> focusables, int count) {
+ return (focused != null && focused.isLayoutRtl()) ?
+ getPreviousFocusable(focused, focusables, count) :
+ getNextFocusable(focused, focusables, count);
+ }
+
+ private View getNextFocusable(View focused, ArrayList<View> focusables, int count) {
+ if (focused != null) {
+ int position = focusables.lastIndexOf(focused);
+ if (position >= 0 && position + 1 < count) {
+ return focusables.get(position + 1);
+ }
+ }
+ return focusables.get(0);
+ }
+
+ private View getBackwardFocusable(View focused, ArrayList<View> focusables, int count) {
+ return (focused != null && focused.isLayoutRtl()) ?
+ getNextFocusable(focused, focusables, count) :
+ getPreviousFocusable(focused, focusables, count);
+ }
+
+ private View getPreviousFocusable(View focused, ArrayList<View> focusables, int count) {
+ if (focused != null) {
+ int position = focusables.indexOf(focused);
+ if (position > 0) {
+ return focusables.get(position - 1);
+ }
+ }
+ return focusables.get(count - 1);
+ }
+
/**
* Is rect1 a better candidate than rect2 for a focus search in a particular
* direction from a source rect? This is the core routine that determines
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 1f75e70..0e96742 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -296,11 +296,11 @@ class GLES20Canvas extends HardwareCanvas {
///////////////////////////////////////////////////////////////////////////
@Override
- public boolean callDrawGLFunction(int drawGLFunction) {
+ public int callDrawGLFunction(int drawGLFunction) {
return nCallDrawGLFunction(mRenderer, drawGLFunction);
}
- private static native boolean nCallDrawGLFunction(int renderer, int drawGLFunction);
+ private static native int nCallDrawGLFunction(int renderer, int drawGLFunction);
///////////////////////////////////////////////////////////////////////////
// Memory
@@ -394,13 +394,13 @@ class GLES20Canvas extends HardwareCanvas {
private static native void nSetDisplayListName(int displayList, String name);
@Override
- public boolean drawDisplayList(DisplayList displayList, int width, int height,
+ public int drawDisplayList(DisplayList displayList, int width, int height,
Rect dirty, int flags) {
return nDrawDisplayList(mRenderer, ((GLES20DisplayList) displayList).getNativeDisplayList(),
width, height, dirty, flags);
}
- private static native boolean nDrawDisplayList(int renderer, int displayList,
+ private static native int nDrawDisplayList(int renderer, int displayList,
int width, int height, Rect dirty, int flags);
@Override
diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java
index 838c03c..2636ea2 100644
--- a/core/java/android/view/HardwareCanvas.java
+++ b/core/java/android/view/HardwareCanvas.java
@@ -59,11 +59,11 @@ public abstract class HardwareCanvas extends Canvas {
* if this method returns true, can be null.
* @param flags Optional flags about drawing, see {@link DisplayList} for
* the possible flags.
- *
- * @return True if the content of the display list requires another
- * drawing pass (invalidate()), false otherwise
+ *
+ * @return One of {@link DisplayList#STATUS_DONE}, {@link DisplayList#STATUS_DRAW} or
+ * {@link DisplayList#STATUS_INVOKE}
*/
- public abstract boolean drawDisplayList(DisplayList displayList, int width, int height,
+ public abstract int drawDisplayList(DisplayList displayList, int width, int height,
Rect dirty, int flags);
/**
@@ -90,10 +90,12 @@ public abstract class HardwareCanvas extends Canvas {
* This function may return true if an invalidation is needed after the call.
*
* @param drawGLFunction A native function pointer
- * @return true if an invalidate is needed after the call, false otherwise
+ *
+ * @return One of {@link DisplayList#STATUS_DONE}, {@link DisplayList#STATUS_DRAW} or
+ * {@link DisplayList#STATUS_INVOKE}
*/
- public boolean callDrawGLFunction(int drawGLFunction) {
+ public int callDrawGLFunction(int drawGLFunction) {
// Noop - this is done in the display list recorder subclass
- return false;
+ return DisplayList.STATUS_DONE;
}
}
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index f98cfc0..d40043f 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -966,7 +966,6 @@ public abstract class HardwareRenderer {
Log.d("DLProperties", "getDisplayList():\t" +
mProfileData[mProfileCurrentFrame]);
}
-
}
if (displayList != null) {
@@ -975,7 +974,7 @@ public abstract class HardwareRenderer {
drawDisplayListStartTime = System.nanoTime();
}
- boolean invalidateNeeded = canvas.drawDisplayList(displayList,
+ int status = canvas.drawDisplayList(displayList,
view.getWidth(), view.getHeight(), mRedrawClip,
DisplayList.FLAG_CLIP_CHILDREN);
@@ -986,19 +985,18 @@ public abstract class HardwareRenderer {
if (ViewDebug.DEBUG_LATENCY) {
Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- drawDisplayList() took " +
- total + "ms, invalidateNeeded=" +
- invalidateNeeded + ".");
+ total + "ms, status=" + status);
}
}
- if (invalidateNeeded) {
+ if (status != DisplayList.STATUS_DONE) {
if (mRedrawClip.isEmpty()) {
attachInfo.mViewRootImpl.invalidate();
} else {
attachInfo.mViewRootImpl.invalidateChildInParent(
null, mRedrawClip);
+ mRedrawClip.setEmpty();
}
- mRedrawClip.setEmpty();
}
} else {
// Shouldn't reach here
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3afc20e..2deeba6 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1459,7 +1459,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
* apps.
* @hide
*/
- public static final boolean USE_DISPLAY_LIST_PROPERTIES = false;
+ public static final boolean USE_DISPLAY_LIST_PROPERTIES = true;
/**
* Map used to store views' tags.
@@ -4933,9 +4933,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
@RemotableViewMethod
public void setLayoutDirection(int layoutDirection) {
if (getLayoutDirection() != layoutDirection) {
- // Reset the current layout direction
+ // Reset the current layout direction and the resolved one
mPrivateFlags2 &= ~LAYOUT_DIRECTION_MASK;
- // Reset the current resolved layout direction
resetResolvedLayoutDirection();
// Set the new layout direction (filtered) and ask for a layout pass
mPrivateFlags2 |=
@@ -4955,7 +4954,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
@ViewDebug.IntToString(from = LAYOUT_DIRECTION_RTL, to = "RESOLVED_DIRECTION_RTL")
})
public int getResolvedLayoutDirection() {
- resolveLayoutDirectionIfNeeded();
+ // The layout diretion will be resolved only if needed
+ if ((mPrivateFlags2 & LAYOUT_DIRECTION_RESOLVED) != LAYOUT_DIRECTION_RESOLVED) {
+ resolveLayoutDirection();
+ }
return ((mPrivateFlags2 & LAYOUT_DIRECTION_RESOLVED_RTL) == LAYOUT_DIRECTION_RESOLVED_RTL) ?
LAYOUT_DIRECTION_RTL : LAYOUT_DIRECTION_LTR;
}
@@ -7399,7 +7401,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
invalidateViewProperty(false, false);
if (USE_DISPLAY_LIST_PROPERTIES && mDisplayList != null) {
- mDisplayList.setCameraDistance(distance);
+ mDisplayList.setCameraDistance(-Math.abs(distance) / dpi);
}
}
@@ -9832,7 +9834,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
jumpDrawablesToCurrentState();
// Order is important here: LayoutDirection MUST be resolved before Padding
// and TextDirection
- resolveLayoutDirectionIfNeeded();
+ resolveLayoutDirection();
resolvePadding();
resolveTextDirection();
if (isFocused()) {
@@ -9863,31 +9865,24 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
/**
* Resolve and cache the layout direction. LTR is set initially. This is implicitly supposing
* that the parent directionality can and will be resolved before its children.
+ * Will call {@link View#onResolvedLayoutDirectionChanged} when resolution is done.
*/
- private void resolveLayoutDirectionIfNeeded() {
- // Do not resolve if it is not needed
- if ((mPrivateFlags2 & LAYOUT_DIRECTION_RESOLVED) == LAYOUT_DIRECTION_RESOLVED) return;
-
+ public void resolveLayoutDirection() {
// Clear any previous layout direction resolution
mPrivateFlags2 &= ~LAYOUT_DIRECTION_RESOLVED_MASK;
// Set resolved depending on layout direction
switch (getLayoutDirection()) {
case LAYOUT_DIRECTION_INHERIT:
- // We cannot do the resolution if there is no parent
- if (mParent == null) return;
-
// If this is root view, no need to look at parent's layout dir.
- if (mParent instanceof ViewGroup) {
+ if (canResolveLayoutDirection()) {
ViewGroup viewGroup = ((ViewGroup) mParent);
- // Check if the parent view group can resolve
- if (! viewGroup.canResolveLayoutDirection()) {
- return;
- }
if (viewGroup.getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) {
mPrivateFlags2 |= LAYOUT_DIRECTION_RESOLVED_RTL;
}
+ } else {
+ // Nothing to do, LTR by default
}
break;
case LAYOUT_DIRECTION_RTL:
@@ -9990,7 +9985,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
public boolean canResolveLayoutDirection() {
switch (getLayoutDirection()) {
case LAYOUT_DIRECTION_INHERIT:
- return (mParent != null);
+ return (mParent != null) && (mParent instanceof ViewGroup);
default:
return true;
}
@@ -10752,16 +10747,25 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
int layerType = (
!(mParent instanceof ViewGroup) || ((ViewGroup)mParent).mDrawLayers) ?
getLayerType() : LAYER_TYPE_NONE;
- if (!isLayer && layerType == LAYER_TYPE_HARDWARE && USE_DISPLAY_LIST_PROPERTIES) {
- final HardwareLayer layer = getHardwareLayer();
- if (layer != null && layer.isValid()) {
- canvas.drawHardwareLayer(layer, 0, 0, mLayerPaint);
+ if (!isLayer && layerType != LAYER_TYPE_NONE && USE_DISPLAY_LIST_PROPERTIES) {
+ if (layerType == LAYER_TYPE_HARDWARE) {
+ final HardwareLayer layer = getHardwareLayer();
+ if (layer != null && layer.isValid()) {
+ canvas.drawHardwareLayer(layer, 0, 0, mLayerPaint);
+ } else {
+ canvas.saveLayer(0, 0, mRight - mLeft, mBottom - mTop, mLayerPaint,
+ Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
+ Canvas.CLIP_TO_LAYER_SAVE_FLAG);
+ }
+ caching = true;
} else {
- canvas.saveLayer(0, 0,
- mRight - mLeft, mBottom - mTop, mLayerPaint,
- Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
+ buildDrawingCache(true);
+ Bitmap cache = getDrawingCache(true);
+ if (cache != null) {
+ canvas.drawBitmap(cache, 0, 0, mLayerPaint);
+ caching = true;
+ }
}
- caching = true;
} else {
computeScroll();
@@ -11400,7 +11404,11 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
mTransformationInfo.mRotation, mTransformationInfo.mRotationX,
mTransformationInfo.mRotationY, mTransformationInfo.mScaleX,
mTransformationInfo.mScaleY);
- displayList.setCameraDistance(getCameraDistance());
+ if (mTransformationInfo.mCamera == null) {
+ mTransformationInfo.mCamera = new Camera();
+ mTransformationInfo.matrix3D = new Matrix();
+ }
+ displayList.setCameraDistance(mTransformationInfo.mCamera.getLocationZ());
if ((mPrivateFlags & PIVOT_EXPLICITLY_SET) == PIVOT_EXPLICITLY_SET) {
displayList.setPivotX(getPivotX());
displayList.setPivotY(getPivotY());
@@ -11494,8 +11502,12 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
} else {
switch (layerType) {
case LAYER_TYPE_SOFTWARE:
- buildDrawingCache(true);
- cache = getDrawingCache(true);
+ if (useDisplayListProperties) {
+ hasDisplayList = canHaveDisplayList();
+ } else {
+ buildDrawingCache(true);
+ cache = getDrawingCache(true);
+ }
break;
case LAYER_TYPE_HARDWARE:
if (useDisplayListProperties) {
@@ -14545,14 +14557,13 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
*/
public void setTextDirection(int textDirection) {
if (getTextDirection() != textDirection) {
- // Reset the current text direction
+ // Reset the current text direction and the resolved one
mPrivateFlags2 &= ~TEXT_DIRECTION_MASK;
+ resetResolvedTextDirection();
// Set the new text direction
mPrivateFlags2 |= ((textDirection << TEXT_DIRECTION_MASK_SHIFT) & TEXT_DIRECTION_MASK);
- // Reset the current resolved text direction
- resetResolvedTextDirection();
- // Ask for a layout pass
requestLayout();
+ invalidate(true);
}
}
@@ -14573,7 +14584,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
* {@link #TEXT_DIRECTION_LOCALE},
*/
public int getResolvedTextDirection() {
- // The text direction is not inherited so return it back
+ // The text direction will be resolved only if needed
if ((mPrivateFlags2 & TEXT_DIRECTION_RESOLVED) != TEXT_DIRECTION_RESOLVED) {
resolveTextDirection();
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 6ccac78..30d6ec7 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3906,9 +3906,15 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
do {
if (parent instanceof ViewGroup) {
ViewGroup parentVG = (ViewGroup) parent;
- parent = parentVG.invalidateChildInParentFast(left, top, dirty);
- left = parentVG.mLeft;
- top = parentVG.mTop;
+ if (parentVG.mLayerType != LAYER_TYPE_NONE) {
+ // Layered parents should be recreated, not just re-issued
+ parentVG.invalidate();
+ parent = null;
+ } else {
+ parent = parentVG.invalidateChildInParentFast(left, top, dirty);
+ left = parentVG.mLeft;
+ top = parentVG.mTop;
+ }
} else {
// Reached the top; this calls into the usual invalidate method in
// ViewRootImpl, which schedules a traversal
@@ -4664,6 +4670,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
public void clearDisappearingChildren() {
if (mDisappearingChildren != null) {
mDisappearingChildren.clear();
+ invalidate();
}
}
@@ -4775,7 +4782,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
view.mParent = null;
}
}
- mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
+ invalidate();
}
}
}
diff --git a/core/java/android/webkit/DebugFlags.java b/core/java/android/webkit/DebugFlags.java
index a21d3ee..349113e 100644
--- a/core/java/android/webkit/DebugFlags.java
+++ b/core/java/android/webkit/DebugFlags.java
@@ -42,12 +42,7 @@ class DebugFlags {
public static final boolean WEB_BACK_FORWARD_LIST = false;
public static final boolean WEB_SETTINGS = false;
public static final boolean WEB_SYNC_MANAGER = false;
- public static final boolean WEB_TEXT_VIEW = false;
public static final boolean WEB_VIEW = false;
public static final boolean WEB_VIEW_CORE = false;
- /*
- * Set to true to allow the WebTextView to draw on top of the web page in a
- * different color with no background so you can see how the two line up.
- */
- public static final boolean DRAW_WEBTEXTVIEW = false;
+ public static final boolean MEASURE_PAGE_SWAP_FPS = false;
}
diff --git a/core/java/android/webkit/FindListener.java b/core/java/android/webkit/FindListener.java
deleted file mode 100644
index 124f737..0000000
--- a/core/java/android/webkit/FindListener.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2012 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.webkit;
-
-/**
- * @hide
- */
-public interface FindListener {
- /**
- * Notify the host application that a find result is available.
- *
- * @param numberOfMatches How many matches have been found
- * @param activeMatchOrdinal The ordinal of the currently selected match
- * @param isDoneCounting Whether we have finished counting matches
- */
- public void onFindResultReceived(int numberOfMatches,
- int activeMatchOrdinal, boolean isDoneCounting);
-}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index dd373de..d225594 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -312,6 +312,24 @@ public class WebView extends AbsoluteLayout
public static final String SCHEME_GEO = "geo:0,0?q=";
/**
+ * Interface to listen for find results.
+ * @hide
+ */
+ public interface FindListener {
+ /**
+ * Notify the listener about progress made by a find operation.
+ *
+ * @param numberOfMatches How many matches have been found.
+ * @param activeMatchOrdinal The zero-based ordinal of the currently selected match.
+ * @param isDoneCounting Whether the find operation has actually completed. The listener
+ * may be notified multiple times while the operation is underway, and the numberOfMatches
+ * value should not be considered final unless isDoneCounting is true.
+ */
+ public void onFindResultReceived(int numberOfMatches, int activeMatchOrdinal,
+ boolean isDoneCounting);
+ }
+
+ /**
* Interface to listen for new pictures as they change.
* @deprecated This interface is now obsolete.
*/
@@ -1228,10 +1246,10 @@ public class WebView extends AbsoluteLayout
}
/**
- * Register the interface to be used when a find-on-page result has become
- * available. This will replace the current handler.
+ * Register the listener to be notified as find-on-page operations progress.
+ * This will replace the current listener.
*
- * @param listener An implementation of FindListener
+ * @param listener An implementation of {@link WebView#FindListener}.
* @hide
*/
public void setFindListener(FindListener listener) {
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index 03329b8..5ae2fe0 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -1440,7 +1440,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
private PictureListener mPictureListener;
// Used to notify listeners about find-on-page results.
- private FindListener mFindListener;
+ private WebView.FindListener mFindListener;
/**
* Refer to {@link WebView#requestFocusNodeHref(Message)} for more information
@@ -3620,12 +3620,10 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Register the interface to be used when a find-on-page result has become
- * available. This will replace the current handler.
- *
- * @param listener An implementation of FindListener
+ * See {@link WebView#setFindListener(WebView.FindListener)}.
+ * @hide
*/
- public void setFindListener(FindListener listener) {
+ public void setFindListener(WebView.FindListener listener) {
mFindListener = listener;
}
@@ -4719,10 +4717,10 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
queueFull = nativeSetBaseLayer(mNativeClass, layer, invalRegion,
showVisualIndicator, isPictureAfterFirstLayout);
- if (layer == 0 || isPictureAfterFirstLayout) {
- mWebViewCore.resumeWebKitDraw();
- } else if (queueFull) {
+ if (queueFull) {
mWebViewCore.pauseWebKitDraw();
+ } else {
+ mWebViewCore.resumeWebKitDraw();
}
if (mHTML5VideoViewProxy != null) {
@@ -8617,9 +8615,19 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
void onPageSwapOccurred(boolean notifyAnimationStarted);
}
+ long mLastSwapTime;
+ double mAverageSwapFps;
+
/** Called by JNI when pages are swapped (only occurs with hardware
* acceleration) */
protected void pageSwapCallback(boolean notifyAnimationStarted) {
+ if (DebugFlags.MEASURE_PAGE_SWAP_FPS) {
+ long now = System.currentTimeMillis();
+ long diff = now - mLastSwapTime;
+ mAverageSwapFps = ((1000.0 / diff) + mAverageSwapFps) / 2;
+ Log.d(LOGTAG, "page swap fps: " + mAverageSwapFps);
+ mLastSwapTime = now;
+ }
mWebViewCore.resumeWebKitDraw();
if (notifyAnimationStarted) {
mWebViewCore.sendMessage(EventHub.NOTIFY_ANIMATION_STARTED);
@@ -8681,7 +8689,12 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
Log.v(LOGTAG, "NEW_PICTURE_MSG_ID {" +
b.left+","+b.top+","+b.right+","+b.bottom+"}");
}
- invalidateContentRect(draw.mInvalRegion.getBounds());
+ Rect invalBounds = draw.mInvalRegion.getBounds();
+ if (!invalBounds.isEmpty()) {
+ invalidateContentRect(invalBounds);
+ } else {
+ mWebView.invalidate();
+ }
if (mPictureListener != null) {
mPictureListener.onNewPicture(getWebView(), capturePicture());
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index b47f71d..afb2992 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -2271,6 +2271,7 @@ public final class WebViewCore {
mFirstLayoutForNonStandardLoad = false;
}
if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
+ pauseWebKitDraw();
Message.obtain(mWebViewClassic.mPrivateHandler,
WebViewClassic.NEW_PICTURE_MSG_ID, draw).sendToTarget();
}
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index 7d47e14..f049198 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -192,7 +192,7 @@ public interface WebViewProvider {
public WebBackForwardList copyBackForwardList();
- public void setFindListener(FindListener listener);
+ public void setFindListener(WebView.FindListener listener);
public void findNext(boolean forward);
diff --git a/core/java/android/widget/AdapterViewFlipper.java b/core/java/android/widget/AdapterViewFlipper.java
index 5096227..aea029b 100644
--- a/core/java/android/widget/AdapterViewFlipper.java
+++ b/core/java/android/widget/AdapterViewFlipper.java
@@ -127,13 +127,29 @@ public class AdapterViewFlipper extends AdapterViewAnimator {
}
/**
- * How long to wait before flipping to the next view
+ * Returns the flip interval, in milliseconds.
*
- * @param milliseconds
- * time in milliseconds
+ * @return the flip interval in milliseconds
+ *
+ * @see #setFlipInterval(int)
+ *
+ * @attr ref android.R.styleable#AdapterViewFlipper_flipInterval
+ */
+ public int getFlipInterval() {
+ return mFlipInterval;
+ }
+
+ /**
+ * How long to wait before flipping to the next view.
+ *
+ * @param flipInterval flip interval in milliseconds
+ *
+ * @see #getFlipInterval()
+ *
+ * @attr ref android.R.styleable#AdapterViewFlipper_flipInterval
*/
- public void setFlipInterval(int milliseconds) {
- mFlipInterval = milliseconds;
+ public void setFlipInterval(int flipInterval) {
+ mFlipInterval = flipInterval;
}
/**
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 3001ea1..b1a75e1 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -223,11 +223,28 @@ public class ImageView extends View {
}
/**
+ * True when ImageView is adjusting its bounds
+ * to preserve the aspect ratio of its drawable
+ *
+ * @return whether to adjust the bounds of this view
+ * to presrve the original aspect ratio of the drawable
+ *
+ * @see #setAdjustViewBounds(boolean)
+ *
+ * @attr ref android.R.styleable#ImageView_adjustViewBounds
+ */
+ public boolean getAdjustViewBounds() {
+ return mAdjustViewBounds;
+ }
+
+ /**
* Set this to true if you want the ImageView to adjust its bounds
* to preserve the aspect ratio of its drawable.
* @param adjustViewBounds Whether to adjust the bounds of this view
* to presrve the original aspect ratio of the drawable
*
+ * @see #getAdjustViewBounds()
+ *
* @attr ref android.R.styleable#ImageView_adjustViewBounds
*/
@android.view.RemotableViewMethod
@@ -237,7 +254,20 @@ public class ImageView extends View {
setScaleType(ScaleType.FIT_CENTER);
}
}
-
+
+ /**
+ * The maximum width of this view.
+ *
+ * @return The maximum width of this view
+ *
+ * @see #setMaxWidth(int)
+ *
+ * @attr ref android.R.styleable#ImageView_maxWidth
+ */
+ public int getMaxWidth() {
+ return mMaxWidth;
+ }
+
/**
* An optional argument to supply a maximum width for this view. Only valid if
* {@link #setAdjustViewBounds(boolean)} has been set to true. To set an image to be a maximum
@@ -253,14 +283,29 @@ public class ImageView extends View {
* </p>
*
* @param maxWidth maximum width for this view
- *
+ *
+ * @see #getMaxWidth()
+ *
* @attr ref android.R.styleable#ImageView_maxWidth
*/
@android.view.RemotableViewMethod
public void setMaxWidth(int maxWidth) {
mMaxWidth = maxWidth;
}
-
+
+ /**
+ * The maximum height of this view.
+ *
+ * @return The maximum height of this view
+ *
+ * @see #setMaxHeight(int)
+ *
+ * @attr ref android.R.styleable#ImageView_maxHeight
+ */
+ public int getMaxHeight() {
+ return mMaxHeight;
+ }
+
/**
* An optional argument to supply a maximum height for this view. Only valid if
* {@link #setAdjustViewBounds(boolean)} has been set to true. To set an image to be a
@@ -276,7 +321,9 @@ public class ImageView extends View {
* </p>
*
* @param maxHeight maximum height for this view
- *
+ *
+ * @see #getMaxHeight()
+ *
* @attr ref android.R.styleable#ImageView_maxHeight
*/
@android.view.RemotableViewMethod
@@ -522,7 +569,37 @@ public class ImageView extends View {
invalidate();
}
}
-
+
+ /**
+ * Return whether this ImageView crops to padding.
+ *
+ * @return whether this ImageView crops to padding
+ *
+ * @see #setCropToPadding(boolean)
+ *
+ * @attr ref android.R.styleable#ImageView_cropToPadding
+ */
+ public boolean getCropToPadding() {
+ return mCropToPadding;
+ }
+
+ /**
+ * Sets whether this ImageView will crop to padding.
+ *
+ * @param cropToPadding whether this ImageView will crop to padding
+ *
+ * @see #getCropToPadding()
+ *
+ * @attr ref android.R.styleable#ImageView_cropToPadding
+ */
+ public void setCropToPadding(boolean cropToPadding) {
+ if (mCropToPadding != cropToPadding) {
+ mCropToPadding = cropToPadding;
+ requestLayout();
+ invalidate();
+ }
+ }
+
private void resolveUri() {
if (mDrawable != null) {
return;
@@ -997,11 +1074,24 @@ public class ImageView extends View {
public final void clearColorFilter() {
setColorFilter(null);
}
-
+
+ /**
+ * Returns the active color filter for this ImageView.
+ *
+ * @return the active color filter for this ImageView
+ *
+ * @see #setColorFilter(android.graphics.ColorFilter)
+ */
+ public ColorFilter getColorFilter() {
+ return mColorFilter;
+ }
+
/**
* Apply an arbitrary colorfilter to the image.
*
* @param cf the colorfilter to apply (may be null)
+ *
+ * @see #getColorFilter()
*/
public void setColorFilter(ColorFilter cf) {
if (mColorFilter != cf) {
@@ -1012,6 +1102,37 @@ public class ImageView extends View {
}
}
+ /**
+ * Returns the alpha that will be applied to the drawable of this ImageView.
+ *
+ * @return the alpha that will be applied to the drawable of this ImageView
+ *
+ * @see #setImageAlpha(int)
+ */
+ public int getImageAlpha() {
+ return mAlpha;
+ }
+
+ /**
+ * Sets the alpha value that should be applied to the image.
+ *
+ * @param alpha the alpha value that should be applied to the image
+ *
+ * @see #getImageAlpha()
+ */
+ @RemotableViewMethod
+ public void setImageAlpha(int alpha) {
+ setAlpha(alpha);
+ }
+
+ /**
+ * Sets the alpha value that should be applied to the image.
+ *
+ * @param alpha the alpha value that should be applied to the image
+ *
+ * @deprecated use #setImageAlpha(int) instead
+ */
+ @Deprecated
@RemotableViewMethod
public void setAlpha(int alpha) {
alpha &= 0xFF; // keep it legal
diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java
index 7f53ffd..f217c9c 100644
--- a/core/java/android/widget/RadioGroup.java
+++ b/core/java/android/widget/RadioGroup.java
@@ -190,6 +190,8 @@ public class RadioGroup extends LinearLayout {
*
* @see #check(int)
* @see #clearCheck()
+ *
+ * @attr ref android.R.styleable#RadioGroup_checkedButton
*/
public int getCheckedRadioButtonId() {
return mCheckedId;
diff --git a/core/java/android/widget/RatingBar.java b/core/java/android/widget/RatingBar.java
index e69577b..524d272 100644
--- a/core/java/android/widget/RatingBar.java
+++ b/core/java/android/widget/RatingBar.java
@@ -145,6 +145,8 @@ public class RatingBar extends AbsSeekBar {
* by the user).
*
* @param isIndicator Whether it should be an indicator.
+ *
+ * @attr ref android.R.styleable#RatingBar_isIndicator
*/
public void setIsIndicator(boolean isIndicator) {
mIsUserSeekable = !isIndicator;
@@ -153,6 +155,8 @@ public class RatingBar extends AbsSeekBar {
/**
* @return Whether this rating bar is only an indicator.
+ *
+ * @attr ref android.R.styleable#RatingBar_isIndicator
*/
public boolean isIndicator() {
return !mIsUserSeekable;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 16d1b94..1f2410b 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -7496,7 +7496,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* Returns true, only while processing a touch gesture, if the initial
* touch down event caused focus to move to the text view and as a result
* its selection changed. Only valid while processing the touch gesture
- * of interest.
+ * of interest, in an editable text view.
*/
public boolean didTouchFocusSelect() {
return mEditor != null && getEditor().mTouchFocusSelected;
@@ -11683,7 +11683,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
highlight = null;
}
- if (false /* TEMP patch for bugs 6198276 & 6193544 */ && canHaveDisplayList() && canvas.isHardwareAccelerated()) {
+ if (canHaveDisplayList() && canvas.isHardwareAccelerated()) {
drawHardwareAccelerated(canvas, layout, highlight, cursorOffsetVertical);
} else {
layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
@@ -11758,7 +11758,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
hardwareCanvas.onPostDraw();
blockDisplayList.end();
if (USE_DISPLAY_LIST_PROPERTIES) {
- blockDisplayList.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
+ blockDisplayList.setLeftTopRightBottom(0, 0, width, height);
}
}
}
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index da189f1..07496a7 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -44,10 +44,14 @@ import java.util.Vector;
* <p>When a state machine is created <code>addState</code> is used to build the
* hierarchy and <code>setInitialState</code> is used to identify which of these
* is the initial state. After construction the programmer calls <code>start</code>
- * which initializes the state machine and calls <code>enter</code> for all of the initial
- * state's hierarchy, starting at its eldest parent. For example given the simple
- * state machine below after start is called mP1.enter will have been called and
- * then mS1.enter.</p>
+ * which initializes and starts the state machine. The first action the StateMachine
+ * is to the invoke <code>enter</code> for all of the initial state's hierarchy,
+ * starting at its eldest parent. The calls to enter will be done in the context
+ * of the StateMachines Handler not in the context of the call to start and they
+ * will be invoked before any messages are processed. For example, given the simple
+ * state machine below mP1.enter will be invoked and then mS1.enter. Finally,
+ * messages sent to the state machine will be processed by the current state,
+ * in our simple state machine below that would initially be mS1.processMessage.</p>
<code>
mP1
/ \
@@ -621,8 +625,8 @@ public class StateMachine {
/** The debug flag */
private boolean mDbg = false;
- /** The quit object */
- private static final Object mQuitObj = new Object();
+ /** The SmHandler object, identifies that message is internal */
+ private static final Object mSmHandlerObj = new Object();
/** The current message */
private Message mMsg;
@@ -726,19 +730,18 @@ public class StateMachine {
/** Save the current message */
mMsg = msg;
- /**
- * Check that construction was completed
- */
- if (!mIsConstructionCompleted) {
- Log.e(TAG, "The start method not called, ignore msg: " + msg);
- return;
+ if (mIsConstructionCompleted) {
+ /** Normal path */
+ processMsg(msg);
+ } else if (!mIsConstructionCompleted &&
+ (mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) {
+ /** Initial one time path. */
+ mIsConstructionCompleted = true;
+ invokeEnterMethods(0);
+ } else {
+ throw new RuntimeException("StateMachine.handleMessage: " +
+ "The start method not called, received msg: " + msg);
}
-
- /**
- * Process the message abiding by the hierarchical semantics
- * and perform any requested transitions.
- */
- processMsg(msg);
performTransitions();
if (mDbg) Log.d(TAG, "handleMessage: X");
@@ -852,18 +855,8 @@ public class StateMachine {
mTempStateStack = new StateInfo[maxDepth];
setupInitialStateStack();
- /**
- * Construction is complete call all enter methods
- * starting at the first entry.
- */
- mIsConstructionCompleted = true;
- mMsg = obtainMessage(SM_INIT_CMD);
- invokeEnterMethods(0);
-
- /**
- * Perform any transitions requested by the enter methods
- */
- performTransitions();
+ /** Sending SM_INIT_CMD message to invoke enter methods asynchronously */
+ sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
if (mDbg) Log.d(TAG, "completeConstruction: X");
}
@@ -1103,14 +1096,14 @@ public class StateMachine {
/** @see StateMachine#setInitialState(State) */
private final void setInitialState(State initialState) {
- if (mDbg) Log.d(TAG, "setInitialState: initialState" + initialState.getName());
+ if (mDbg) Log.d(TAG, "setInitialState: initialState=" + initialState.getName());
mInitialState = initialState;
}
/** @see StateMachine#transitionTo(IState) */
private final void transitionTo(IState destState) {
mDestState = (State) destState;
- if (mDbg) Log.d(TAG, "StateMachine.transitionTo EX destState" + mDestState.getName());
+ if (mDbg) Log.d(TAG, "transitionTo: destState=" + mDestState.getName());
}
/** @see StateMachine#deferMessage(Message) */
@@ -1127,12 +1120,12 @@ public class StateMachine {
/** @see StateMachine#deferMessage(Message) */
private final void quit() {
if (mDbg) Log.d(TAG, "quit:");
- sendMessage(obtainMessage(SM_QUIT_CMD, mQuitObj));
+ sendMessage(obtainMessage(SM_QUIT_CMD, mSmHandlerObj));
}
/** @see StateMachine#isQuit(Message) */
private final boolean isQuit(Message msg) {
- return (msg.what == SM_QUIT_CMD) && (msg.obj == mQuitObj);
+ return (msg.what == SM_QUIT_CMD) && (msg.obj == mSmHandlerObj);
}
/** @see StateMachine#isDbg() */
diff --git a/core/java/com/google/android/mms/pdu/PduPersister.java b/core/java/com/google/android/mms/pdu/PduPersister.java
index b04f890..7c937ed 100644
--- a/core/java/com/google/android/mms/pdu/PduPersister.java
+++ b/core/java/com/google/android/mms/pdu/PduPersister.java
@@ -20,6 +20,8 @@ package com.google.android.mms.pdu;
import com.google.android.mms.ContentType;
import com.google.android.mms.InvalidHeaderValueException;
import com.google.android.mms.MmsException;
+import com.google.android.mms.util.DownloadDrmHelper;
+import com.google.android.mms.util.DrmConvertSession;
import com.google.android.mms.util.PduCache;
import com.google.android.mms.util.PduCacheEntry;
import com.google.android.mms.util.SqliteWrapper;
@@ -30,7 +32,11 @@ import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteException;
+import android.drm.DrmManagerClient;
import android.net.Uri;
+import android.os.FileUtils;
+import android.provider.MediaStore;
import android.provider.Telephony;
import android.provider.Telephony.Mms;
import android.provider.Telephony.MmsSms;
@@ -42,6 +48,7 @@ import android.text.TextUtils;
import android.util.Log;
import java.io.ByteArrayOutputStream;
+import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
@@ -271,10 +278,12 @@ public class PduPersister {
private final Context mContext;
private final ContentResolver mContentResolver;
+ private final DrmManagerClient mDrmManagerClient;
private PduPersister(Context context) {
mContext = context;
mContentResolver = context.getContentResolver();
+ mDrmManagerClient = new DrmManagerClient(context);
}
/** Get(or create if not exist) an instance of PduPersister */
@@ -761,6 +770,9 @@ public class PduPersister {
throws MmsException {
OutputStream os = null;
InputStream is = null;
+ DrmConvertSession drmConvertSession = null;
+ Uri dataUri = null;
+ String path = null;
try {
byte[] data = part.getData();
@@ -773,9 +785,38 @@ public class PduPersister {
throw new MmsException("unable to update " + uri.toString());
}
} else {
+ boolean isDrm = DownloadDrmHelper.isDrmConvertNeeded(contentType);
+ if (isDrm) {
+ if (uri != null) {
+ try {
+ path = convertUriToPath(mContext, uri);
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "drm uri: " + uri + " path: " + path);
+ }
+ File f = new File(path);
+ long len = f.length();
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "drm path: " + path + " len: " + len);
+ }
+ if (len > 0) {
+ // we're not going to re-persist and re-encrypt an already
+ // converted drm file
+ return;
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Can't get file info for: " + part.getDataUri(), e);
+ }
+ }
+ // We haven't converted the file yet, start the conversion
+ drmConvertSession = DrmConvertSession.open(mContext, contentType);
+ if (drmConvertSession == null) {
+ throw new MmsException("Mimetype " + contentType +
+ " can not be converted.");
+ }
+ }
os = mContentResolver.openOutputStream(uri);
if (data == null) {
- Uri dataUri = part.getDataUri();
+ dataUri = part.getDataUri();
if ((dataUri == null) || (dataUri == uri)) {
Log.w(TAG, "Can't find data for this part.");
return;
@@ -788,13 +829,32 @@ public class PduPersister {
byte[] buffer = new byte[8192];
for (int len = 0; (len = is.read(buffer)) != -1; ) {
- os.write(buffer, 0, len);
+ if (!isDrm) {
+ os.write(buffer, 0, len);
+ } else {
+ byte[] convertedData = drmConvertSession.convert(buffer, len);
+ if (convertedData != null) {
+ os.write(convertedData, 0, convertedData.length);
+ } else {
+ throw new MmsException("Error converting drm data.");
+ }
+ }
}
} else {
if (LOCAL_LOGV) {
Log.v(TAG, "Saving data to: " + uri);
}
- os.write(data);
+ if (!isDrm) {
+ os.write(data);
+ } else {
+ dataUri = uri;
+ byte[] convertedData = drmConvertSession.convert(data, data.length);
+ if (convertedData != null) {
+ os.write(convertedData, 0, convertedData.length);
+ } else {
+ throw new MmsException("Error converting drm data.");
+ }
+ }
}
}
} catch (FileNotFoundException e) {
@@ -818,7 +878,65 @@ public class PduPersister {
Log.e(TAG, "IOException while closing: " + is, e);
} // Ignore
}
+ if (drmConvertSession != null) {
+ drmConvertSession.close(path);
+
+ // Reset the permissions on the encrypted part file so everyone has only read
+ // permission.
+ File f = new File(path);
+ ContentValues values = new ContentValues(0);
+ SqliteWrapper.update(mContext, mContentResolver,
+ Uri.parse("content://mms/resetFilePerm/" + f.getName()),
+ values, null, null);
+ }
+ }
+ }
+
+ /**
+ * This method expects uri in the following format
+ * content://media/<table_name>/<row_index> (or)
+ * file://sdcard/test.mp4
+ * http://test.com/test.mp4
+ *
+ * Here <table_name> shall be "video" or "audio" or "images"
+ * <row_index> the index of the content in given table
+ */
+ static public String convertUriToPath(Context context, Uri uri) {
+ String path = null;
+ if (null != uri) {
+ String scheme = uri.getScheme();
+ if (null == scheme || scheme.equals("") ||
+ scheme.equals(ContentResolver.SCHEME_FILE)) {
+ path = uri.getPath();
+
+ } else if (scheme.equals("http")) {
+ path = uri.toString();
+
+ } else if (scheme.equals(ContentResolver.SCHEME_CONTENT)) {
+ String[] projection = new String[] {MediaStore.MediaColumns.DATA};
+ Cursor cursor = null;
+ try {
+ cursor = context.getContentResolver().query(uri, projection, null,
+ null, null);
+ if (null == cursor || 0 == cursor.getCount() || !cursor.moveToFirst()) {
+ throw new IllegalArgumentException("Given Uri could not be found" +
+ " in media store");
+ }
+ int pathIndex = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
+ path = cursor.getString(pathIndex);
+ } catch (SQLiteException e) {
+ throw new IllegalArgumentException("Given Uri is not formatted in a way " +
+ "so that it can be found in media store.");
+ } finally {
+ if (null != cursor) {
+ cursor.close();
+ }
+ }
+ } else {
+ throw new IllegalArgumentException("Given Uri scheme is not supported");
+ }
}
+ return path;
}
private void updateAddress(
diff --git a/core/java/com/google/android/mms/util/DownloadDrmHelper.java b/core/java/com/google/android/mms/util/DownloadDrmHelper.java
new file mode 100644
index 0000000..6852eca
--- /dev/null
+++ b/core/java/com/google/android/mms/util/DownloadDrmHelper.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2012 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.google.android.mms.util;
+
+import android.content.Context;
+import android.drm.DrmManagerClient;
+import android.util.Log;
+
+public class DownloadDrmHelper {
+ private static final String TAG = "DownloadDrmHelper";
+
+ /** The MIME type of special DRM files */
+ public static final String MIMETYPE_DRM_MESSAGE = "application/vnd.oma.drm.message";
+
+ /** The extensions of special DRM files */
+ public static final String EXTENSION_DRM_MESSAGE = ".dm";
+
+ public static final String EXTENSION_INTERNAL_FWDL = ".fl";
+
+ /**
+ * Checks if the Media Type is a DRM Media Type
+ *
+ * @param drmManagerClient A DrmManagerClient
+ * @param mimetype Media Type to check
+ * @return True if the Media Type is DRM else false
+ */
+ public static boolean isDrmMimeType(Context context, String mimetype) {
+ boolean result = false;
+ if (context != null) {
+ try {
+ DrmManagerClient drmClient = new DrmManagerClient(context);
+ if (drmClient != null && mimetype != null && mimetype.length() > 0) {
+ result = drmClient.canHandle("", mimetype);
+ }
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG,
+ "DrmManagerClient instance could not be created, context is Illegal.");
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "DrmManagerClient didn't initialize properly.");
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Checks if the Media Type needs to be DRM converted
+ *
+ * @param mimetype Media type of the content
+ * @return True if convert is needed else false
+ */
+ public static boolean isDrmConvertNeeded(String mimetype) {
+ return MIMETYPE_DRM_MESSAGE.equals(mimetype);
+ }
+
+ /**
+ * Modifies the file extension for a DRM Forward Lock file NOTE: This
+ * function shouldn't be called if the file shouldn't be DRM converted
+ */
+ public static String modifyDrmFwLockFileExtension(String filename) {
+ if (filename != null) {
+ int extensionIndex;
+ extensionIndex = filename.lastIndexOf(".");
+ if (extensionIndex != -1) {
+ filename = filename.substring(0, extensionIndex);
+ }
+ filename = filename.concat(EXTENSION_INTERNAL_FWDL);
+ }
+ return filename;
+ }
+
+ /**
+ * Gets the original mime type of DRM protected content.
+ *
+ * @param context The context
+ * @param path Path to the file
+ * @param containingMime The current mime type of of the file i.e. the
+ * containing mime type
+ * @return The original mime type of the file if DRM protected else the
+ * currentMime
+ */
+ public static String getOriginalMimeType(Context context, String path, String containingMime) {
+ String result = containingMime;
+ DrmManagerClient drmClient = new DrmManagerClient(context);
+ try {
+ if (drmClient.canHandle(path, null)) {
+ result = drmClient.getOriginalMimeType(path);
+ }
+ } catch (IllegalArgumentException ex) {
+ Log.w(TAG,
+ "Can't get original mime type since path is null or empty string.");
+ } catch (IllegalStateException ex) {
+ Log.w(TAG, "DrmManagerClient didn't initialize properly.");
+ }
+ return result;
+ }
+}
diff --git a/core/java/com/google/android/mms/util/DrmConvertSession.java b/core/java/com/google/android/mms/util/DrmConvertSession.java
new file mode 100644
index 0000000..2d8f274
--- /dev/null
+++ b/core/java/com/google/android/mms/util/DrmConvertSession.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2012 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.google.android.mms.util;
+
+import android.content.Context;
+import android.drm.DrmConvertedStatus;
+import android.drm.DrmManagerClient;
+import android.util.Log;
+import android.provider.Downloads;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+
+public class DrmConvertSession {
+ private DrmManagerClient mDrmClient;
+ private int mConvertSessionId;
+ private static final String TAG = "DrmConvertSession";
+
+ private DrmConvertSession(DrmManagerClient drmClient, int convertSessionId) {
+ mDrmClient = drmClient;
+ mConvertSessionId = convertSessionId;
+ }
+
+ /**
+ * Start of converting a file.
+ *
+ * @param context The context of the application running the convert session.
+ * @param mimeType Mimetype of content that shall be converted.
+ * @return A convert session or null in case an error occurs.
+ */
+ public static DrmConvertSession open(Context context, String mimeType) {
+ DrmManagerClient drmClient = null;
+ int convertSessionId = -1;
+ if (context != null && mimeType != null && !mimeType.equals("")) {
+ try {
+ drmClient = new DrmManagerClient(context);
+ try {
+ convertSessionId = drmClient.openConvertSession(mimeType);
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "Conversion of Mimetype: " + mimeType
+ + " is not supported.", e);
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Could not access Open DrmFramework.", e);
+ }
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG,
+ "DrmManagerClient instance could not be created, context is Illegal.");
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "DrmManagerClient didn't initialize properly.");
+ }
+ }
+
+ if (drmClient == null || convertSessionId < 0) {
+ return null;
+ } else {
+ return new DrmConvertSession(drmClient, convertSessionId);
+ }
+ }
+ /**
+ * Convert a buffer of data to protected format.
+ *
+ * @param buffer Buffer filled with data to convert.
+ * @param size The number of bytes that shall be converted.
+ * @return A Buffer filled with converted data, if execution is ok, in all
+ * other case null.
+ */
+ public byte [] convert(byte[] inBuffer, int size) {
+ byte[] result = null;
+ if (inBuffer != null) {
+ DrmConvertedStatus convertedStatus = null;
+ try {
+ if (size != inBuffer.length) {
+ byte[] buf = new byte[size];
+ System.arraycopy(inBuffer, 0, buf, 0, size);
+ convertedStatus = mDrmClient.convertData(mConvertSessionId, buf);
+ } else {
+ convertedStatus = mDrmClient.convertData(mConvertSessionId, inBuffer);
+ }
+
+ if (convertedStatus != null &&
+ convertedStatus.statusCode == DrmConvertedStatus.STATUS_OK &&
+ convertedStatus.convertedData != null) {
+ result = convertedStatus.convertedData;
+ }
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "Buffer with data to convert is illegal. Convertsession: "
+ + mConvertSessionId, e);
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Could not convert data. Convertsession: " +
+ mConvertSessionId, e);
+ }
+ } else {
+ throw new IllegalArgumentException("Parameter inBuffer is null");
+ }
+ return result;
+ }
+
+ /**
+ * Ends a conversion session of a file.
+ *
+ * @param fileName The filename of the converted file.
+ * @return Downloads.Impl.STATUS_SUCCESS if execution is ok.
+ * Downloads.Impl.STATUS_FILE_ERROR in case converted file can not
+ * be accessed. Downloads.Impl.STATUS_NOT_ACCEPTABLE if a problem
+ * occurs when accessing drm framework.
+ * Downloads.Impl.STATUS_UNKNOWN_ERROR if a general error occurred.
+ */
+ public int close(String filename) {
+ DrmConvertedStatus convertedStatus = null;
+ int result = Downloads.Impl.STATUS_UNKNOWN_ERROR;
+ if (mDrmClient != null && mConvertSessionId >= 0) {
+ try {
+ convertedStatus = mDrmClient.closeConvertSession(mConvertSessionId);
+ if (convertedStatus == null ||
+ convertedStatus.statusCode != DrmConvertedStatus.STATUS_OK ||
+ convertedStatus.convertedData == null) {
+ result = Downloads.Impl.STATUS_NOT_ACCEPTABLE;
+ } else {
+ RandomAccessFile rndAccessFile = null;
+ try {
+ rndAccessFile = new RandomAccessFile(filename, "rw");
+ rndAccessFile.seek(convertedStatus.offset);
+ rndAccessFile.write(convertedStatus.convertedData);
+ result = Downloads.Impl.STATUS_SUCCESS;
+ } catch (FileNotFoundException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "File: " + filename + " could not be found.", e);
+ } catch (IOException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "Could not access File: " + filename + " .", e);
+ } catch (IllegalArgumentException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "Could not open file in mode: rw", e);
+ } catch (SecurityException e) {
+ Log.w(TAG, "Access to File: " + filename +
+ " was denied denied by SecurityManager.", e);
+ } finally {
+ if (rndAccessFile != null) {
+ try {
+ rndAccessFile.close();
+ } catch (IOException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "Failed to close File:" + filename
+ + ".", e);
+ }
+ }
+ }
+ }
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Could not close convertsession. Convertsession: " +
+ mConvertSessionId, e);
+ }
+ }
+ return result;
+ }
+}
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 8e5db36..baf296d 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -21,12 +21,16 @@
#include "jni.h"
#include "GraphicsJNI.h"
#include <nativehelper/JNIHelp.h>
+
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_graphics_SurfaceTexture.h>
-#include <cutils/properties.h>
+#include <gui/SurfaceTexture.h>
+
#include <androidfw/ResourceTypes.h>
-#include <gui/SurfaceTexture.h>
+#include <private/hwui/DrawGlInfo.h>
+
+#include <cutils/properties.h>
#include <SkBitmap.h>
#include <SkCanvas.h>
@@ -196,7 +200,7 @@ static jint android_view_GLES20Canvas_getStencilSize(JNIEnv* env, jobject clazz)
// Functor
// ----------------------------------------------------------------------------
-static bool android_view_GLES20Canvas_callDrawGLFunction(JNIEnv* env, jobject clazz,
+static jint android_view_GLES20Canvas_callDrawGLFunction(JNIEnv* env, jobject clazz,
OpenGLRenderer* renderer, Functor *functor) {
android::uirenderer::Rect dirty;
return renderer->callDrawGLFunction(functor, dirty);
@@ -682,16 +686,16 @@ static void android_view_GLES20Canvas_destroyDisplayList(JNIEnv* env,
DisplayList::destroyDisplayListDeferred(displayList);
}
-static bool android_view_GLES20Canvas_drawDisplayList(JNIEnv* env,
+static jint android_view_GLES20Canvas_drawDisplayList(JNIEnv* env,
jobject clazz, OpenGLRenderer* renderer, DisplayList* displayList,
jint width, jint height, jobject dirty, jint flags) {
android::uirenderer::Rect bounds;
- bool redraw = renderer->drawDisplayList(displayList, width, height, bounds, flags);
- if (redraw && dirty != NULL) {
+ status_t status = renderer->drawDisplayList(displayList, width, height, bounds, flags);
+ if (status != DrawGlInfo::kStatusDone && dirty != NULL) {
env->CallVoidMethod(dirty, gRectClassInfo.set,
int(bounds.left), int(bounds.top), int(bounds.right), int(bounds.bottom));
}
- return redraw;
+ return status;
}
static void android_view_GLES20Canvas_outputDisplayList(JNIEnv* env,
@@ -865,7 +869,7 @@ static JNINativeMethod gMethods[] = {
{ "nGetStencilSize", "()I", (void*) android_view_GLES20Canvas_getStencilSize },
- { "nCallDrawGLFunction", "(II)Z",
+ { "nCallDrawGLFunction", "(II)I",
(void*) android_view_GLES20Canvas_callDrawGLFunction },
{ "nSave", "(II)I", (void*) android_view_GLES20Canvas_save },
@@ -943,7 +947,7 @@ static JNINativeMethod gMethods[] = {
{ "nGetDisplayListSize", "(I)I", (void*) android_view_GLES20Canvas_getDisplayListSize },
{ "nSetDisplayListName", "(ILjava/lang/String;)V",
(void*) android_view_GLES20Canvas_setDisplayListName },
- { "nDrawDisplayList", "(IIIILandroid/graphics/Rect;I)Z",
+ { "nDrawDisplayList", "(IIIILandroid/graphics/Rect;I)I",
(void*) android_view_GLES20Canvas_drawDisplayList },
{ "nCreateDisplayListRenderer", "()I", (void*) android_view_GLES20Canvas_createDisplayListRenderer },
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d1e3642..5ae12b6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -60,6 +60,9 @@
<protected-broadcast android:name="android.intent.action.REBOOT" />
<protected-broadcast android:name="android.intent.action.DOCK_EVENT" />
<protected-broadcast android:name="android.intent.action.MASTER_CLEAR_NOTIFICATION" />
+ <protected-broadcast android:name="android.intent.action.USER_ADDED" />
+ <protected-broadcast android:name="android.intent.action.USER_REMOVED" />
+ <protected-broadcast android:name="android.intent.action.USER_SWITCHED" />
<protected-broadcast android:name="android.app.action.ENTER_CAR_MODE" />
<protected-broadcast android:name="android.app.action.EXIT_CAR_MODE" />
@@ -1032,15 +1035,6 @@
android:label="@string/permlab_clearAppCache"
android:description="@string/permdesc_clearAppCache" />
- <!-- Allows an application to read the low-level system log files.
- Log entries can contain the user's private information,
- which is why this permission is 'dangerous'. -->
- <permission android:name="android.permission.READ_LOGS"
- android:permissionGroup="android.permission-group.PERSONAL_INFO"
- android:protectionLevel="dangerous"
- android:label="@string/permlab_readLogs"
- android:description="@string/permdesc_readLogs" />
-
<!-- Allows an application to use any media decoder when decoding for playback
@hide -->
<permission android:name="android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"
@@ -1076,6 +1070,15 @@
android:label="@string/permlab_dump"
android:description="@string/permdesc_dump" />
+ <!-- Allows an application to read the low-level system log files.
+ Log entries can contain the user's private information,
+ which is why this permission is not available to normal apps. -->
+ <permission android:name="android.permission.READ_LOGS"
+ android:permissionGroup="android.permission-group.DEVELOPMENT_TOOLS"
+ android:protectionLevel="signature|system|development"
+ android:label="@string/permlab_readLogs"
+ android:description="@string/permdesc_readLogs" />
+
<!-- Configure an application for debugging. -->
<permission android:name="android.permission.SET_DEBUG_APP"
android:permissionGroup="android.permission-group.DEVELOPMENT_TOOLS"
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index f1379f7..0481a67 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -342,9 +342,9 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"ስለ ገቢ እና ወጪ ጥሪዎችን ውሂብ ጨምሮ፣ የጡባዊተኮህን ምዝግብ ማስታወሻ ለመቀየር ለመተግበሪያው ይፈቅዳል። ይሄንን ተንኮል አዘል መተግበሪያዎች የስልክህን ምዝግብ ማስታወሻ ለመሰረዝ ወይም ለመለወጥ ሊጠቀሙበት ይችላሉ።"</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"ስለ ገቢ እና ወጪ ጥሪዎችን ውሂብ ጨምሮ፣ የስልክህን ምዝግብ ማስታወሻ ለመቀየር ለመተግበሪያው ይፈቅዳል። ይሄንን ተንኮል አዘል መተግበሪያዎች የስልክህን ምዝግብ ማስታወሻ ለመሰረዝ ወይም ለመለወጥ ሊጠቀሙበት ይችላሉ።"</string>
<string name="permlab_readProfile" msgid="6824681438529842282">"የመገለጫ ውሂብዎን ያንብቡ"</string>
- <string name="permdesc_readProfile" product="default" msgid="94520753797630679">"ልክ እንደ አንተ ስም እና የዕውቂያ መረጃ ፣ ባንተ መሳሪያ ወስጥ የተከማቹ የግል መገለጫ መረጃ ለማንበብ ለመተግበሪያው ይፈቅዳሉ፡፡ይሄም ማለት ሌሎች መተግበሪያዎች ሊለዩህ ይችላሉ እና ለሌሎች የመገለጫ መረጃህን ይልካሉ፡፡"</string>
+ <string name="permdesc_readProfile" product="default" msgid="94520753797630679">"ልክ እንደ አንተ ስም እና የዕውቂያ መረጃ ፣ በአንተ መሳሪያ ወስጥ የተከማቹ የግል መገለጫ መረጃ ለማንበብ ለመተግበሪያው ይፈቅዳሉ፡፡ይሄም ማለት ሌሎች መተግበሪያዎች ሊለዩህ ይችላሉ እና ለሌሎች የመገለጫ መረጃህን ይልካሉ፡፡"</string>
<string name="permlab_writeProfile" msgid="4679878325177177400">"የአርስዎ መገለጫ ውሂብ ላይ ይፃፉ"</string>
- <string name="permdesc_writeProfile" product="default" msgid="4637366723793045603">"ልክ እንደ አንተ ስም እና የዕውቂያ መረጃ ፣ ባንተ መሳሪያ ወስጥ የተከማቹ የግል መገለጫ መረጃ ለመለወጥ ወይም ለማከል ለመተግበሪያው ይፈቅዳሉ፡፡ይሄም ማለት ሌሎች መተግበሪያዎች ሊለዩህ ይችላሉ እና ለሌሎች የመገለጫ መረጃህን ይልካሉ፡፡"</string>
+ <string name="permdesc_writeProfile" product="default" msgid="4637366723793045603">"ልክ እንደ አንተ ስም እና የዕውቂያ መረጃ ፣ በአንተ መሳሪያ ወስጥ የተከማቹ የግል መገለጫ መረጃ ለመለወጥ ወይም ለማከል ለመተግበሪያው ይፈቅዳሉ፡፡ይሄም ማለት ሌሎች መተግበሪያዎች ሊለዩህ ይችላሉ እና ለሌሎች የመገለጫ መረጃህን ይልካሉ፡፡"</string>
<string name="permlab_readSocialStream" product="default" msgid="1268920956152419170">"የአንተን ማህበራዊ የውይይት ክፍሎች አንብብ"</string>
<string name="permdesc_readSocialStream" product="default" msgid="3419050808547335320">" ከአንተ ጓደኞች ማህበራዊ ዝማኔዎችን እንዲደርስባቸው እና እንዲያመሳስል ለመተግበሪያውይፈቅዳሉ፡፡ ተንኮል አዘል መተግበሪያዎች ይህን መዳረሻ ባንተና በጓደኞችህ መካከል በማህበራዊ አውታረመረቦች ያሉ የግል ተግባቦቶችን ለመዳረስ ሊጠቀሙበት ይችላሉ፡፡"</string>
<string name="permlab_writeSocialStream" product="default" msgid="3504179222493235645">"ወደ የአንተ ማህበራዊ የውይይት ክፍሎች ጻፍ"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 8384b48..55a298e 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -108,7 +108,7 @@
<string name="fcComplete" msgid="3118848230966886575">"Kode fitur selesai."</string>
<string name="fcError" msgid="3327560126588500777">"Masalah sambungan atau kode fitur tidak valid."</string>
<string name="httpErrorOk" msgid="1191919378083472204">"OK"</string>
- <string name="httpError" msgid="7956392511146698522">"Terjadi galat jaringan."</string>
+ <string name="httpError" msgid="7956392511146698522">"Terjadi kesalahan jaringan."</string>
<string name="httpErrorLookup" msgid="4711687456111963163">"Tidak dapat menemukan URL."</string>
<string name="httpErrorUnsupportedAuthScheme" msgid="6299980280442076799">"Skema autentikasi situs tidak didukung."</string>
<string name="httpErrorAuth" msgid="1435065629438044534">"Tidak dapat mengautentikasi."</string>
@@ -123,7 +123,7 @@
<string name="httpErrorFile" msgid="2170788515052558676">"Tidak dapat mengakses file."</string>
<string name="httpErrorFileNotFound" msgid="6203856612042655084">"Tidak dapat menemukan file yang diminta."</string>
<string name="httpErrorTooManyRequests" msgid="1235396927087188253">"Terlalu banyak permintaan yang diproses. Coba lagi nanti."</string>
- <string name="notification_title" msgid="8967710025036163822">"Galat saat masuk untuk <xliff:g id="ACCOUNT">%1$s</xliff:g>"</string>
+ <string name="notification_title" msgid="8967710025036163822">"Kesalahan saat masuk untuk <xliff:g id="ACCOUNT">%1$s</xliff:g>"</string>
<string name="contentServiceSync" msgid="8353523060269335667">"Sinkron"</string>
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Sinkron"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Terlalu banyak <xliff:g id="CONTENT_TYPE">%s</xliff:g> penghapusan."</string>
@@ -1061,7 +1061,7 @@
<string name="candidates_style" msgid="4333913089637062257"><u>"calon"</u></string>
<string name="ext_media_checking_notification_title" product="nosdcard" msgid="3449816005351468560">"Menyiapkan penyimpanan USB"</string>
<string name="ext_media_checking_notification_title" product="default" msgid="5457603418970994050">"Menyiapkan kartu SD"</string>
- <string name="ext_media_checking_notification_message" msgid="8287319882926737053">"Memeriksa galat."</string>
+ <string name="ext_media_checking_notification_message" msgid="8287319882926737053">"Memeriksa kesalahan."</string>
<string name="ext_media_nofs_notification_title" product="nosdcard" msgid="7788040745686229307">"Penyimpanan USB kosong"</string>
<string name="ext_media_nofs_notification_title" product="default" msgid="780477838241212997">"Kartu SD kosong"</string>
<string name="ext_media_nofs_notification_message" product="nosdcard" msgid="7840121067427269500">"Penyimpanan USB kosong atau sistem berkasnya tidak didukung."</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 3fc2f34..447af8a 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -743,8 +743,7 @@
<string name="double_tap_toast" msgid="4595046515400268881">"提示:輕按兩下即可縮放。"</string>
<string name="autofill_this_form" msgid="4616758841157816676">"自動填入功能"</string>
<string name="setup_autofill" msgid="7103495070180590814">"設定自動填入功能"</string>
- <!-- no translation found for autofill_address_name_separator (6350145154779706772) -->
- <skip />
+ <string name="autofill_address_name_separator" msgid="6350145154779706772">" //*** Empty segment here as a separator ***//"</string>
<string name="autofill_address_summary_name_format" msgid="3268041054899214945">"$1$2$3"</string>
<string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string>
<string name="autofill_address_summary_format" msgid="4874459455786827344">"$1$2$3"</string>
diff --git a/core/tests/coretests/src/android/net/SSLTest.java b/core/tests/coretests/src/android/net/SSLTest.java
index 810ed0d..c573498 100644
--- a/core/tests/coretests/src/android/net/SSLTest.java
+++ b/core/tests/coretests/src/android/net/SSLTest.java
@@ -16,17 +16,16 @@
package android.net;
-import android.net.SSLCertificateSocketFactory;
import android.test.suitebuilder.annotation.Suppress;
-import junit.framework.TestCase;
-
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
+import java.util.Arrays;
+import junit.framework.TestCase;
-//This test relies on network resources.
-@Suppress
public class SSLTest extends TestCase {
+ //This test relies on network resources.
+ @Suppress
public void testCertificate() throws Exception {
// test www.fortify.net/sslcheck.html
Socket ssl = SSLCertificateSocketFactory.getDefault().createSocket("www.fortify.net",443);
@@ -49,4 +48,35 @@ public class SSLTest extends TestCase {
// System.out.println(new String(b));
}
+
+ public void testStringsToNpnBytes() {
+ byte[] expected = {
+ 6, 's', 'p', 'd', 'y', '/', '2',
+ 8, 'h', 't', 't', 'p', '/', '1', '.', '1',
+ };
+ assertTrue(Arrays.equals(expected, SSLCertificateSocketFactory.toNpnProtocolsList(
+ new byte[] { 's', 'p', 'd', 'y', '/', '2' },
+ new byte[] { 'h', 't', 't', 'p', '/', '1', '.', '1' })));
+ }
+
+ public void testStringsToNpnBytesEmptyByteArray() {
+ try {
+ SSLCertificateSocketFactory.toNpnProtocolsList(new byte[0]);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testStringsToNpnBytesEmptyArray() {
+ byte[] expected = {};
+ assertTrue(Arrays.equals(expected, SSLCertificateSocketFactory.toNpnProtocolsList()));
+ }
+
+ public void testStringsToNpnBytesOversizedInput() {
+ try {
+ SSLCertificateSocketFactory.toNpnProtocolsList(new byte[256]);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
}