diff options
Diffstat (limited to 'core')
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) { + } + } } |