diff options
Diffstat (limited to 'core/java')
86 files changed, 3510 insertions, 7737 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index e95fbfc..aa1c70e 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -743,6 +743,7 @@ public class Activity extends ContextThemeWrapper final FragmentManagerImpl mFragments = new FragmentManagerImpl(); final FragmentContainer mContainer = new FragmentContainer() { @Override + @Nullable public View findViewById(int id) { return Activity.this.findViewById(id); } @@ -2068,6 +2069,7 @@ public class Activity extends ContextThemeWrapper * * @return The view if found or null otherwise. */ + @Nullable public View findViewById(int id) { return getWindow().findViewById(id); } diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 70e6e5a..a3662b2 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -18,6 +18,7 @@ package android.app; import com.android.internal.app.WindowDecorActionBar; +import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.content.ContextWrapper; @@ -476,6 +477,7 @@ public class Dialog implements DialogInterface, Window.Callback, * @param id the identifier of the view to find * @return The view with the given id or null. */ + @Nullable public View findViewById(int id) { return mWindow.findViewById(id); } diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index ab28d95..f319309 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -2008,6 +2008,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene mChildFragmentManager = new FragmentManagerImpl(); mChildFragmentManager.attachActivity(mActivity, new FragmentContainer() { @Override + @Nullable public View findViewById(int id) { if (mView == null) { throw new IllegalStateException("Fragment does not have a view"); diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index ccceef4..afdc917 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -19,6 +19,7 @@ package android.app; import android.animation.Animator; import android.animation.AnimatorInflater; import android.animation.AnimatorListenerAdapter; +import android.annotation.Nullable; import android.content.Context; import android.content.res.Configuration; import android.content.res.TypedArray; @@ -394,6 +395,7 @@ final class FragmentManagerState implements Parcelable { * Callbacks from FragmentManagerImpl to its container. */ interface FragmentContainer { + @Nullable public View findViewById(int id); public boolean hasView(); } diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 88b9080..5d864df 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -75,6 +75,7 @@ interface INotificationManager ZenModeConfig getZenModeConfig(); boolean setZenModeConfig(in ZenModeConfig config); + oneway void setZenMode(int mode); oneway void notifyConditions(String pkg, in IConditionProvider provider, in Condition[] conditions); oneway void requestZenModeConditions(in IConditionListener callback, int relevance); oneway void setZenModeCondition(in Condition condition); diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index cf54107..479327d 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -27,6 +27,9 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.StrictMode; import android.os.UserHandle; +import android.service.notification.Condition; +import android.service.notification.IConditionListener; +import android.service.notification.ZenModeConfig; import android.util.Log; /** @@ -276,5 +279,53 @@ public class NotificationManager } } + /** + * @hide + */ + public void setZenMode(int mode) { + INotificationManager service = getService(); + try { + service.setZenMode(mode); + } catch (RemoteException e) { + } + } + + /** + * @hide + */ + public void requestZenModeConditions(IConditionListener listener, int relevance) { + INotificationManager service = getService(); + try { + service.requestZenModeConditions(listener, relevance); + } catch (RemoteException e) { + } + } + + /** + * @hide + */ + public void setZenModeCondition(Condition exitCondition) { + INotificationManager service = getService(); + try { + service.setZenModeCondition(exitCondition); + } catch (RemoteException e) { + } + } + + /** + * @hide + */ + public Condition getZenModeCondition() { + INotificationManager service = getService(); + try { + final ZenModeConfig config = service.getZenModeConfig(); + if (config != null) { + return config.exitCondition; + } + } catch (RemoteException e) { + } + return null; + } + private Context mContext; } diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java index 0a255f7..0f6ce12 100644 --- a/core/java/android/app/UiModeManager.java +++ b/core/java/android/app/UiModeManager.java @@ -219,10 +219,9 @@ public class UiModeManager { } /** - * Returns the currently configured night mode. - * - * @return {@link #MODE_NIGHT_NO}, {@link #MODE_NIGHT_YES}, or - * {@link #MODE_NIGHT_AUTO}. When an error occurred -1 is returned. + * @return the currently configured night mode. May be one of + * {@link #MODE_NIGHT_NO}, {@link #MODE_NIGHT_YES}, + * {@link #MODE_NIGHT_AUTO}, or -1 on error. */ public int getNightMode() { if (mService != null) { diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java index e9cce51..381d851 100644 --- a/core/java/android/app/admin/DeviceAdminReceiver.java +++ b/core/java/android/app/admin/DeviceAdminReceiver.java @@ -170,10 +170,10 @@ public class DeviceAdminReceiver extends BroadcastReceiver { * lock task mode from an authorized package. The extra {@link #EXTRA_LOCK_TASK_PACKAGE} * will describe the authorized package using lock task mode. * - * @see DevicePolicyManager#isLockTaskPermitted(String) - * * <p>The calling device admin must be the device owner or profile * owner to receive this broadcast. + * + * @see DevicePolicyManager#isLockTaskPermitted(String) */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_LOCK_TASK_ENTERING @@ -183,20 +183,19 @@ public class DeviceAdminReceiver extends BroadcastReceiver { * Action sent to a device administrator to notify that the device is exiting * lock task mode from an authorized package. * - * @see DevicePolicyManager#isLockTaskPermitted(String) - * * <p>The calling device admin must be the device owner or profile * owner to receive this broadcast. + * + * @see DevicePolicyManager#isLockTaskPermitted(String) */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_LOCK_TASK_EXITING = "android.app.action.LOCK_TASK_EXITING"; /** - * A boolean describing whether the device is currently entering or exiting - * lock task mode. + * A string containing the name of the package entering lock task mode. * - * @see #ACTION_LOCK_TASK_CHANGED + * @see #ACTION_LOCK_TASK_ENTERING */ public static final String EXTRA_LOCK_TASK_PACKAGE = "android.app.extra.LOCK_TASK_PACKAGE"; diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index dec2524..df620d0 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2149,17 +2149,21 @@ public abstract class Context { WIFI_PASSPOINT_SERVICE, WIFI_P2P_SERVICE, WIFI_SCANNING_SERVICE, + //@hide: WIFI_RTT_SERVICE, //@hide: ETHERNET_SERVICE, WIFI_RTT_SERVICE, NSD_SERVICE, AUDIO_SERVICE, + //@hide: FINGERPRINT_SERVICE, MEDIA_ROUTER_SERVICE, TELEPHONY_SERVICE, + TELEPHONY_SUBSCRIPTION_SERVICE, TELECOM_SERVICE, CLIPBOARD_SERVICE, INPUT_METHOD_SERVICE, TEXT_SERVICES_MANAGER_SERVICE, APPWIDGET_SERVICE, + //@hide: VOICE_INTERACTION_MANAGER_SERVICE, //@hide: BACKUP_SERVICE, DROPBOX_SERVICE, DEVICE_POLICY_SERVICE, @@ -2171,16 +2175,23 @@ public abstract class Context { USB_SERVICE, LAUNCHER_APPS_SERVICE, //@hide: SERIAL_SERVICE, + //@hide: HDMI_CONTROL_SERVICE, INPUT_SERVICE, DISPLAY_SERVICE, - //@hide: SCHEDULING_POLICY_SERVICE, USER_SERVICE, - //@hide: APP_OPS_SERVICE + RESTRICTIONS_SERVICE, + APP_OPS_SERVICE, CAMERA_SERVICE, PRINT_SERVICE, + CONSUMER_IR_SERVICE, + //@hide: TRUST_SERVICE, + TV_INPUT_SERVICE, + //@hide: NETWORK_SCORE_SERVICE, + USAGE_STATS_SERVICE, MEDIA_SESSION_SERVICE, BATTERY_SERVICE, JOB_SCHEDULER_SERVICE, + //@hide: PERSISTENT_DATA_BLOCK_SERVICE, MEDIA_PROJECTION_SERVICE, MIDI_SERVICE, }) diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java index a386097..c2d2f65 100644 --- a/core/java/android/content/pm/RegisteredServicesCache.java +++ b/core/java/android/content/pm/RegisteredServicesCache.java @@ -27,6 +27,7 @@ import android.content.res.XmlResourceParser; import android.os.Environment; import android.os.Handler; import android.os.UserHandle; +import android.os.UserManager; import android.util.AtomicFile; import android.util.AttributeSet; import android.util.Log; @@ -47,9 +48,9 @@ import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileDescriptor; -import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; @@ -74,6 +75,7 @@ import libcore.io.IoUtils; public abstract class RegisteredServicesCache<V> { private static final String TAG = "PackageManager"; private static final boolean DEBUG = false; + protected static final String REGISTERED_SERVICES_DIR = "registered_services"; public final Context mContext; private final String mInterfaceName; @@ -84,57 +86,67 @@ public abstract class RegisteredServicesCache<V> { private final Object mServicesLock = new Object(); @GuardedBy("mServicesLock") - private boolean mPersistentServicesFileDidNotExist; - @GuardedBy("mServicesLock") private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>(2); private static class UserServices<V> { @GuardedBy("mServicesLock") - public final Map<V, Integer> persistentServices = Maps.newHashMap(); + final Map<V, Integer> persistentServices = Maps.newHashMap(); + @GuardedBy("mServicesLock") + Map<V, ServiceInfo<V>> services = null; @GuardedBy("mServicesLock") - public Map<V, ServiceInfo<V>> services = null; + boolean mPersistentServicesFileDidNotExist = true; } + @GuardedBy("mServicesLock") private UserServices<V> findOrCreateUserLocked(int userId) { + return findOrCreateUserLocked(userId, true); + } + + @GuardedBy("mServicesLock") + private UserServices<V> findOrCreateUserLocked(int userId, boolean loadFromFileIfNew) { UserServices<V> services = mUserServices.get(userId); if (services == null) { services = new UserServices<V>(); mUserServices.put(userId, services); + if (loadFromFileIfNew && mSerializerAndParser != null) { + // Check if user exists and try loading data from file + // clear existing data if there was an error during migration + UserInfo user = getUser(userId); + if (user != null) { + AtomicFile file = createFileForUser(user.id); + if (file.getBaseFile().exists()) { + if (DEBUG) { + Slog.i(TAG, String.format("Loading u%s data from %s", user.id, file)); + } + InputStream is = null; + try { + is = file.openRead(); + readPersistentServicesLocked(is); + } catch (Exception e) { + Log.w(TAG, "Error reading persistent services for user " + user.id, e); + } finally { + IoUtils.closeQuietly(is); + } + } + } + } } return services; } - /** - * This file contains the list of known services. We would like to maintain this forever - * so we store it as an XML file. - */ - private final AtomicFile mPersistentServicesFile; - // the listener and handler are synchronized on "this" and must be updated together private RegisteredServicesCacheListener<V> mListener; private Handler mHandler; public RegisteredServicesCache(Context context, String interfaceName, String metaDataName, String attributeName, XmlSerializerAndParser<V> serializerAndParser) { - this(context, interfaceName, metaDataName, attributeName, serializerAndParser, - Environment.getDataDirectory()); - } - - @VisibleForTesting - protected RegisteredServicesCache(Context context, String interfaceName, String metaDataName, - String attributeName, XmlSerializerAndParser<V> serializerAndParser, File dataDir) { mContext = context; mInterfaceName = interfaceName; mMetaDataName = metaDataName; mAttributesName = attributeName; mSerializerAndParser = serializerAndParser; - File systemDir = new File(dataDir, "system"); - File syncDir = new File(systemDir, "registered_services"); - mPersistentServicesFile = new AtomicFile(new File(syncDir, interfaceName + ".xml")); - - // Load persisted services from disk - readPersistentServicesLocked(); + migrateIfNecessaryLocked(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); @@ -148,6 +160,11 @@ public abstract class RegisteredServicesCache<V> { sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); mContext.registerReceiver(mExternalReceiver, sdFilter); + + // Register for user-related events + IntentFilter userFilter = new IntentFilter(); + sdFilter.addAction(Intent.ACTION_USER_REMOVED); + mContext.registerReceiver(mUserRemovedReceiver, userFilter); } private final void handlePackageEvent(Intent intent, int userId) { @@ -199,6 +216,17 @@ public abstract class RegisteredServicesCache<V> { } }; + private final BroadcastReceiver mUserRemovedReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); + if (DEBUG) { + Slog.d(TAG, "u" + userId + " removed - cleaning up"); + } + onUserRemoved(userId); + } + }; + public void invalidateCache(int userId) { synchronized (mServicesLock) { final UserServices<V> user = findOrCreateUserLocked(userId); @@ -390,7 +418,7 @@ public abstract class RegisteredServicesCache<V> { changed = true; user.services.put(info.type, info); user.persistentServices.put(info.type, info.uid); - if (!(mPersistentServicesFileDidNotExist && firstScan)) { + if (!(user.mPersistentServicesFileDidNotExist && firstScan)) { notifyListener(info.type, userId, false /* removed */); } } else if (previousUid == info.uid) { @@ -460,7 +488,7 @@ public abstract class RegisteredServicesCache<V> { } } if (changed) { - writePersistentServicesLocked(); + writePersistentServicesLocked(user, userId); } } } @@ -542,89 +570,152 @@ public abstract class RegisteredServicesCache<V> { /** * Read all sync status back in to the initial engine state. */ - private void readPersistentServicesLocked() { - mUserServices.clear(); + private void readPersistentServicesLocked(InputStream is) + throws XmlPullParserException, IOException { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(is, null); + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.START_TAG + && eventType != XmlPullParser.END_DOCUMENT) { + eventType = parser.next(); + } + String tagName = parser.getName(); + if ("services".equals(tagName)) { + eventType = parser.next(); + do { + if (eventType == XmlPullParser.START_TAG && parser.getDepth() == 2) { + tagName = parser.getName(); + if ("service".equals(tagName)) { + V service = mSerializerAndParser.createFromXml(parser); + if (service == null) { + break; + } + String uidString = parser.getAttributeValue(null, "uid"); + final int uid = Integer.parseInt(uidString); + final int userId = UserHandle.getUserId(uid); + final UserServices<V> user = findOrCreateUserLocked(userId, + false /*loadFromFileIfNew*/) ; + user.persistentServices.put(service, uid); + } + } + eventType = parser.next(); + } while (eventType != XmlPullParser.END_DOCUMENT); + } + } + + private void migrateIfNecessaryLocked() { if (mSerializerAndParser == null) { return; } - FileInputStream fis = null; - try { - mPersistentServicesFileDidNotExist = !mPersistentServicesFile.getBaseFile().exists(); - if (mPersistentServicesFileDidNotExist) { - return; - } - fis = mPersistentServicesFile.openRead(); - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(fis, null); - int eventType = parser.getEventType(); - while (eventType != XmlPullParser.START_TAG - && eventType != XmlPullParser.END_DOCUMENT) { - eventType = parser.next(); - } - String tagName = parser.getName(); - if ("services".equals(tagName)) { - eventType = parser.next(); - do { - if (eventType == XmlPullParser.START_TAG && parser.getDepth() == 2) { - tagName = parser.getName(); - if ("service".equals(tagName)) { - V service = mSerializerAndParser.createFromXml(parser); - if (service == null) { - break; + File systemDir = new File(getDataDirectory(), "system"); + File syncDir = new File(systemDir, REGISTERED_SERVICES_DIR); + AtomicFile oldFile = new AtomicFile(new File(syncDir, mInterfaceName + ".xml")); + boolean oldFileExists = oldFile.getBaseFile().exists(); + + if (oldFileExists) { + File marker = new File(syncDir, mInterfaceName + ".xml.migrated"); + // if not migrated, perform the migration and add a marker + if (!marker.exists()) { + if (DEBUG) { + Slog.i(TAG, "Marker file " + marker + " does not exist - running migration"); + } + InputStream is = null; + try { + is = oldFile.openRead(); + mUserServices.clear(); + readPersistentServicesLocked(is); + } catch (Exception e) { + Log.w(TAG, "Error reading persistent services, starting from scratch", e); + } finally { + IoUtils.closeQuietly(is); + } + try { + for (UserInfo user : getUsers()) { + UserServices<V> userServices = mUserServices.get(user.id); + if (userServices != null) { + if (DEBUG) { + Slog.i(TAG, "Migrating u" + user.id + " services " + + userServices.persistentServices); } - String uidString = parser.getAttributeValue(null, "uid"); - final int uid = Integer.parseInt(uidString); - final int userId = UserHandle.getUserId(uid); - final UserServices<V> user = findOrCreateUserLocked(userId); - user.persistentServices.put(service, uid); + writePersistentServicesLocked(userServices, user.id); } } - eventType = parser.next(); - } while (eventType != XmlPullParser.END_DOCUMENT); + marker.createNewFile(); + } catch (Exception e) { + Log.w(TAG, "Migration failed", e); + } + // Migration is complete and we don't need to keep data for all users anymore, + // It will be loaded from a new location when requested + mUserServices.clear(); } - } catch (Exception e) { - Log.w(TAG, "Error reading persistent services, starting from scratch", e); - } finally { - IoUtils.closeQuietly(fis); } } /** - * Write all sync status to the sync status file. + * Writes services of a specified user to the file. */ - private void writePersistentServicesLocked() { + private void writePersistentServicesLocked(UserServices<V> user, int userId) { if (mSerializerAndParser == null) { return; } + AtomicFile atomicFile = createFileForUser(userId); FileOutputStream fos = null; try { - fos = mPersistentServicesFile.startWrite(); + fos = atomicFile.startWrite(); XmlSerializer out = new FastXmlSerializer(); out.setOutput(fos, "utf-8"); out.startDocument(null, true); out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); out.startTag(null, "services"); - for (int i = 0; i < mUserServices.size(); i++) { - final UserServices<V> user = mUserServices.valueAt(i); - for (Map.Entry<V, Integer> service : user.persistentServices.entrySet()) { - out.startTag(null, "service"); - out.attribute(null, "uid", Integer.toString(service.getValue())); - mSerializerAndParser.writeAsXml(service.getKey(), out); - out.endTag(null, "service"); - } + for (Map.Entry<V, Integer> service : user.persistentServices.entrySet()) { + out.startTag(null, "service"); + out.attribute(null, "uid", Integer.toString(service.getValue())); + mSerializerAndParser.writeAsXml(service.getKey(), out); + out.endTag(null, "service"); } out.endTag(null, "services"); out.endDocument(); - mPersistentServicesFile.finishWrite(fos); + atomicFile.finishWrite(fos); } catch (IOException e1) { Log.w(TAG, "Error writing accounts", e1); if (fos != null) { - mPersistentServicesFile.failWrite(fos); + atomicFile.failWrite(fos); } } } @VisibleForTesting + protected void onUserRemoved(int userId) { + mUserServices.remove(userId); + } + + @VisibleForTesting + protected List<UserInfo> getUsers() { + return UserManager.get(mContext).getUsers(true); + } + + @VisibleForTesting + protected UserInfo getUser(int userId) { + return UserManager.get(mContext).getUserInfo(userId); + } + + private AtomicFile createFileForUser(int userId) { + File userDir = getUserSystemDirectory(userId); + File userFile = new File(userDir, REGISTERED_SERVICES_DIR + "/" + mInterfaceName + ".xml"); + return new AtomicFile(userFile); + } + + @VisibleForTesting + protected File getUserSystemDirectory(int userId) { + return Environment.getUserSystemDirectory(userId); + } + + @VisibleForTesting + protected File getDataDirectory() { + return Environment.getDataDirectory(); + } + + @VisibleForTesting protected Map<V, Integer> getPersistentServices(int userId) { return findOrCreateUserLocked(userId).persistentServices; } diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java index c125544..e61664c 100644 --- a/core/java/android/database/DatabaseUtils.java +++ b/core/java/android/database/DatabaseUtils.java @@ -16,8 +16,6 @@ package android.database; -import org.apache.commons.codec.binary.Hex; - import android.content.ContentValues; import android.content.Context; import android.content.OperationApplicationException; @@ -416,11 +414,33 @@ public class DatabaseUtils { * @return the collation key in hex format */ public static String getHexCollationKey(String name) { - byte [] arr = getCollationKeyInBytes(name); - char[] keys = Hex.encodeHex(arr); + byte[] arr = getCollationKeyInBytes(name); + char[] keys = encodeHex(arr); return new String(keys, 0, getKeyLen(arr) * 2); } + + /** + * Used building output as Hex + */ + private static final char[] DIGITS = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + + private static char[] encodeHex(byte[] input) { + int l = input.length; + char[] out = new char[l << 1]; + + // two characters form the hex value. + for (int i = 0, j = 0; i < l; i++) { + out[j++] = DIGITS[(0xF0 & input[i]) >>> 4 ]; + out[j++] = DIGITS[ 0x0F & input[i] ]; + } + + return out; + } + private static int getKeyLen(byte[] arr) { if (arr[arr.length - 1] != 0) { return arr.length; diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index fa5e9d2..39f4cca 100644 --- a/core/java/android/hardware/Sensor.java +++ b/core/java/android/hardware/Sensor.java @@ -833,4 +833,96 @@ public final class Sensor { + ", type=" + mType + ", maxRange=" + mMaxRange + ", resolution=" + mResolution + ", power=" + mPower + ", minDelay=" + mMinDelay + "}"; } + + /** + * Sets the Type associated with the sensor. + * NOTE: to be used only by native bindings in SensorManager. + * + * This allows interned static strings to be used across all representations of the Sensor. If + * a sensor type is not referenced here, it will still be interned by the native SensorManager. + * + * @return {@code true} if the StringType was successfully set, {@code false} otherwise. + */ + private boolean setType(int value) { + mType = value; + switch (mType) { + case TYPE_ACCELEROMETER: + mStringType = STRING_TYPE_ACCELEROMETER; + return true; + case TYPE_AMBIENT_TEMPERATURE: + mStringType = STRING_TYPE_AMBIENT_TEMPERATURE; + return true; + case TYPE_GAME_ROTATION_VECTOR: + mStringType = STRING_TYPE_GAME_ROTATION_VECTOR; + return true; + case TYPE_GEOMAGNETIC_ROTATION_VECTOR: + mStringType = STRING_TYPE_GEOMAGNETIC_ROTATION_VECTOR; + return true; + case TYPE_GLANCE_GESTURE: + mStringType = STRING_TYPE_GLANCE_GESTURE; + return true; + case TYPE_GRAVITY: + mStringType = STRING_TYPE_GRAVITY; + return true; + case TYPE_GYROSCOPE: + mStringType = STRING_TYPE_GYROSCOPE; + return true; + case TYPE_GYROSCOPE_UNCALIBRATED: + mStringType = STRING_TYPE_GYROSCOPE_UNCALIBRATED; + return true; + case TYPE_HEART_RATE: + mStringType = STRING_TYPE_HEART_RATE; + return true; + case TYPE_LIGHT: + mStringType = STRING_TYPE_LIGHT; + return true; + case TYPE_LINEAR_ACCELERATION: + mStringType = STRING_TYPE_LINEAR_ACCELERATION; + return true; + case TYPE_MAGNETIC_FIELD: + mStringType = STRING_TYPE_MAGNETIC_FIELD; + return true; + case TYPE_MAGNETIC_FIELD_UNCALIBRATED: + mStringType = STRING_TYPE_MAGNETIC_FIELD_UNCALIBRATED; + return true; + case TYPE_PICK_UP_GESTURE: + mStringType = STRING_TYPE_PICK_UP_GESTURE; + return true; + case TYPE_PRESSURE: + mStringType = STRING_TYPE_PRESSURE; + return true; + case TYPE_PROXIMITY: + mStringType = STRING_TYPE_PROXIMITY; + return true; + case TYPE_RELATIVE_HUMIDITY: + mStringType = STRING_TYPE_RELATIVE_HUMIDITY; + return true; + case TYPE_ROTATION_VECTOR: + mStringType = STRING_TYPE_ROTATION_VECTOR; + return true; + case TYPE_SIGNIFICANT_MOTION: + mStringType = STRING_TYPE_SIGNIFICANT_MOTION; + return true; + case TYPE_STEP_COUNTER: + mStringType = STRING_TYPE_STEP_COUNTER; + return true; + case TYPE_STEP_DETECTOR: + mStringType = STRING_TYPE_STEP_DETECTOR; + return true; + case TYPE_TILT_DETECTOR: + mStringType = SENSOR_STRING_TYPE_TILT_DETECTOR; + return true; + case TYPE_WAKE_GESTURE: + mStringType = STRING_TYPE_WAKE_GESTURE; + return true; + case TYPE_ORIENTATION: + mStringType = STRING_TYPE_ORIENTATION; + return true; + case TYPE_TEMPERATURE: + mStringType = STRING_TYPE_TEMPERATURE; + return true; + default: + return false; + } + } } diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java index a0a0716..615b2c8 100644 --- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java +++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java @@ -269,21 +269,23 @@ public class SurfaceTextureRenderer { throw new IllegalStateException("Illegal intermediate texture with dimension of 0"); } - // Letterbox or pillerbox output dimensions into intermediate dimensions. + // Letterbox or pillar-box output dimensions into intermediate dimensions. RectF intermediate = new RectF(/*left*/0, /*top*/0, /*right*/texWidth, /*bottom*/texHeight); RectF output = new RectF(/*left*/0, /*top*/0, /*right*/width, /*bottom*/height); android.graphics.Matrix boxingXform = new android.graphics.Matrix(); boxingXform.setRectToRect(output, intermediate, android.graphics.Matrix.ScaleToFit.CENTER); boxingXform.mapRect(output); - // Find scaling factor from pillerboxed/letterboxed output dimensions to intermediate + // Find scaling factor from pillar-boxed/letter-boxed output dimensions to intermediate // buffer dimensions. float scaleX = intermediate.width() / output.width(); float scaleY = intermediate.height() / output.height(); - // Scale opposite dimension in clip coordinates so output is letterboxed/pillerboxed into - // the intermediate dimensions (rather than vice-versa). - Matrix.scaleM(mMVPMatrix, /*offset*/0, /*x*/scaleY, /*y*/scaleX, /*z*/1); + // Intermediate texture is implicitly scaled to 'fill' the output dimensions in clip space + // coordinates in the shader. To avoid stretching, we need to scale the larger dimension + // of the intermediate buffer so that the output buffer is actually letter-boxed + // or pillar-boxed into the intermediate buffer after clipping. + Matrix.scaleM(mMVPMatrix, /*offset*/0, /*x*/scaleX, /*y*/scaleY, /*z*/1); if (DEBUG) { Log.d(TAG, "Scaling factors (S_x = " + scaleX + ",S_y = " + scaleY + ") used for " + diff --git a/core/java/android/midi/MidiDeviceServer.java b/core/java/android/midi/MidiDeviceServer.java index 7499934..4a1995f 100644 --- a/core/java/android/midi/MidiDeviceServer.java +++ b/core/java/android/midi/MidiDeviceServer.java @@ -254,12 +254,12 @@ public final class MidiDeviceServer implements Closeable { return new MidiReceiver() { @Override - public void onPost(byte[] msg, int offset, int count, long timestamp) throws IOException { + public void post(byte[] msg, int offset, int count, long timestamp) throws IOException { ArrayList<MidiInputPort> receivers = mOutputPortReceivers[portNumberF]; synchronized (receivers) { for (int i = 0; i < receivers.size(); i++) { // FIXME catch errors and remove dead ones - receivers.get(i).onPost(msg, offset, count, timestamp); + receivers.get(i).post(msg, offset, count, timestamp); } } } diff --git a/core/java/android/midi/MidiInputPort.java b/core/java/android/midi/MidiInputPort.java index 51c47dd..735c68a 100644 --- a/core/java/android/midi/MidiInputPort.java +++ b/core/java/android/midi/MidiInputPort.java @@ -50,7 +50,7 @@ public class MidiInputPort extends MidiPort implements MidiReceiver { * @param timestamp future time to post the message (based on * {@link java.lang.System#nanoTime} */ - public void onPost(byte[] msg, int offset, int count, long timestamp) throws IOException { + public void post(byte[] msg, int offset, int count, long timestamp) throws IOException { assert(offset >= 0 && count >= 0 && offset + count <= msg.length); synchronized (mBuffer) { diff --git a/core/java/android/midi/MidiManager.java b/core/java/android/midi/MidiManager.java index 8aa8395..3a0b064 100644 --- a/core/java/android/midi/MidiManager.java +++ b/core/java/android/midi/MidiManager.java @@ -66,22 +66,24 @@ public class MidiManager { } /** - * Callback interface used for clients to receive MIDI device added and removed notifications + * Callback class used for clients to receive MIDI device added and removed notifications */ - public interface DeviceCallback { + public static class DeviceCallback { /** * Called to notify when a new MIDI device has been added * * @param device a {@link MidiDeviceInfo} for the newly added device */ - void onDeviceAdded(MidiDeviceInfo device); + void onDeviceAdded(MidiDeviceInfo device) { + } /** * Called to notify when a MIDI device has been removed * * @param device a {@link MidiDeviceInfo} for the removed device */ - void onDeviceRemoved(MidiDeviceInfo device); + void onDeviceRemoved(MidiDeviceInfo device) { + } } /** diff --git a/core/java/android/midi/MidiOutputPort.java b/core/java/android/midi/MidiOutputPort.java index 332b431..b9512fd 100644 --- a/core/java/android/midi/MidiOutputPort.java +++ b/core/java/android/midi/MidiOutputPort.java @@ -65,7 +65,7 @@ public class MidiOutputPort extends MidiPort implements MidiSender { for (int i = 0; i < mReceivers.size(); i++) { MidiReceiver receiver = mReceivers.get(i); try { - receiver.onPost(buffer, offset, size, timestamp); + receiver.post(buffer, offset, size, timestamp); } catch (IOException e) { Log.e(TAG, "post failed"); deadReceivers.add(receiver); diff --git a/core/java/android/midi/MidiReceiver.java b/core/java/android/midi/MidiReceiver.java index fdfe51a..16c9bbb 100644 --- a/core/java/android/midi/MidiReceiver.java +++ b/core/java/android/midi/MidiReceiver.java @@ -32,7 +32,7 @@ public interface MidiReceiver { * The msg bytes should be copied by the receiver rather than retaining a reference * to this parameter. * Also, modifying the contents of the msg array parameter may result in other receivers - * in the same application receiving incorrect values in their onPost() method. + * in the same application receiving incorrect values in their post() method. * * @param msg a byte array containing the MIDI data * @param offset the offset of the first byte of the data in the byte array @@ -40,5 +40,5 @@ public interface MidiReceiver { * @param timestamp the timestamp of the message (based on {@link java.lang.System#nanoTime} * @throws IOException */ - public void onPost(byte[] msg, int offset, int count, long timestamp) throws IOException; + public void post(byte[] msg, int offset, int count, long timestamp) throws IOException; } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 1a51808..808be21 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -2371,6 +2371,10 @@ public class ConnectivityManager { * The lookup key for a {@link Network} object included with the intent after * successfully finding a network for the applications request. Retrieve it with * {@link android.content.Intent#getParcelableExtra(String)}. + * <p> + * Note that if you intend to invoke (@link #setProcessDefaultNetwork(Network)) or + * {@link Network#openConnection(java.net.URL)} then you must get a + * ConnectivityManager instance before doing so. */ public static final String EXTRA_NETWORK = "android.net.extra.NETWORK"; diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java index a3cad77..2c90909 100644 --- a/core/java/android/net/ProxyInfo.java +++ b/core/java/android/net/ProxyInfo.java @@ -21,8 +21,6 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; -import org.apache.http.client.HttpClient; - import java.net.InetSocketAddress; import java.net.URLConnection; import java.util.List; @@ -31,8 +29,9 @@ import java.util.Locale; /** * Describes a proxy configuration. * - * Proxy configurations are already integrated within the Apache HTTP stack. - * So {@link URLConnection} and {@link HttpClient} will use them automatically. + * Proxy configurations are already integrated within the {@code java.net} and + * Apache HTTP stack. So {@link URLConnection} and Apache's {@code HttpClient} will use + * them automatically. * * Other HTTP stacks will need to obtain the proxy info from * {@link Proxy#PROXY_CHANGE_ACTION} broadcast as the extra {@link Proxy#EXTRA_PROXY_INFO}. diff --git a/core/java/android/net/http/AndroidHttpClient.java b/core/java/android/net/http/AndroidHttpClient.java deleted file mode 100644 index a262076..0000000 --- a/core/java/android/net/http/AndroidHttpClient.java +++ /dev/null @@ -1,527 +0,0 @@ -/* - * Copyright (C) 2007 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.net.http; - -import com.android.internal.http.HttpDateTime; - -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.HttpEntityEnclosingRequest; -import org.apache.http.HttpException; -import org.apache.http.HttpHost; -import org.apache.http.HttpRequest; -import org.apache.http.HttpRequestInterceptor; -import org.apache.http.HttpResponse; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.HttpClient; -import org.apache.http.client.ResponseHandler; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.client.params.HttpClientParams; -import org.apache.http.client.protocol.ClientContext; -import org.apache.http.conn.ClientConnectionManager; -import org.apache.http.conn.scheme.PlainSocketFactory; -import org.apache.http.conn.scheme.Scheme; -import org.apache.http.conn.scheme.SchemeRegistry; -import org.apache.http.entity.AbstractHttpEntity; -import org.apache.http.entity.ByteArrayEntity; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.impl.client.RequestWrapper; -import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.HttpConnectionParams; -import org.apache.http.params.HttpParams; -import org.apache.http.params.HttpProtocolParams; -import org.apache.http.protocol.BasicHttpContext; -import org.apache.http.protocol.BasicHttpProcessor; -import org.apache.http.protocol.HttpContext; - -import android.content.ContentResolver; -import android.content.Context; -import android.net.SSLCertificateSocketFactory; -import android.net.SSLSessionCache; -import android.os.Looper; -import android.util.Base64; -import android.util.Log; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.URI; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; - -/** - * Implementation of the Apache {@link DefaultHttpClient} that is configured with - * reasonable default settings and registered schemes for Android. - * Don't create this directly, use the {@link #newInstance} factory method. - * - * <p>This client processes cookies but does not retain them by default. - * To retain cookies, simply add a cookie store to the HttpContext:</p> - * - * <pre>context.setAttribute(ClientContext.COOKIE_STORE, cookieStore);</pre> - * - * @deprecated Please use {@link java.net.URLConnection} and friends instead. - * The Apache HTTP client is no longer maintained and may be removed in a future - * release. Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a> - * for further details. - */ -@Deprecated -public final class AndroidHttpClient implements HttpClient { - - // Gzip of data shorter than this probably won't be worthwhile - public static long DEFAULT_SYNC_MIN_GZIP_BYTES = 256; - - // Default connection and socket timeout of 60 seconds. Tweak to taste. - private static final int SOCKET_OPERATION_TIMEOUT = 60 * 1000; - - private static final String TAG = "AndroidHttpClient"; - - private static String[] textContentTypes = new String[] { - "text/", - "application/xml", - "application/json" - }; - - /** Interceptor throws an exception if the executing thread is blocked */ - private static final HttpRequestInterceptor sThreadCheckInterceptor = - new HttpRequestInterceptor() { - public void process(HttpRequest request, HttpContext context) { - // Prevent the HttpRequest from being sent on the main thread - if (Looper.myLooper() != null && Looper.myLooper() == Looper.getMainLooper() ) { - throw new RuntimeException("This thread forbids HTTP requests"); - } - } - }; - - /** - * Create a new HttpClient with reasonable defaults (which you can update). - * - * @param userAgent to report in your HTTP requests - * @param context to use for caching SSL sessions (may be null for no caching) - * @return AndroidHttpClient for you to use for all your requests. - * - * @deprecated Please use {@link java.net.URLConnection} and friends instead. See - * {@link android.net.SSLCertificateSocketFactory} for SSL cache support. If you'd - * like to set a custom useragent, please use {@link java.net.URLConnection#setRequestProperty(String, String)} - * with {@code field} set to {@code User-Agent}. - */ - @Deprecated - public static AndroidHttpClient newInstance(String userAgent, Context context) { - HttpParams params = new BasicHttpParams(); - - // Turn off stale checking. Our connections break all the time anyway, - // and it's not worth it to pay the penalty of checking every time. - HttpConnectionParams.setStaleCheckingEnabled(params, false); - - HttpConnectionParams.setConnectionTimeout(params, SOCKET_OPERATION_TIMEOUT); - HttpConnectionParams.setSoTimeout(params, SOCKET_OPERATION_TIMEOUT); - HttpConnectionParams.setSocketBufferSize(params, 8192); - - // Don't handle redirects -- return them to the caller. Our code - // often wants to re-POST after a redirect, which we must do ourselves. - HttpClientParams.setRedirecting(params, false); - - // Use a session cache for SSL sockets - SSLSessionCache sessionCache = context == null ? null : new SSLSessionCache(context); - - // Set the specified user agent and register standard protocols. - HttpProtocolParams.setUserAgent(params, userAgent); - SchemeRegistry schemeRegistry = new SchemeRegistry(); - schemeRegistry.register(new Scheme("http", - PlainSocketFactory.getSocketFactory(), 80)); - schemeRegistry.register(new Scheme("https", - SSLCertificateSocketFactory.getHttpSocketFactory( - SOCKET_OPERATION_TIMEOUT, sessionCache), 443)); - - ClientConnectionManager manager = - new ThreadSafeClientConnManager(params, schemeRegistry); - - // We use a factory method to modify superclass initialization - // parameters without the funny call-a-static-method dance. - return new AndroidHttpClient(manager, params); - } - - /** - * Create a new HttpClient with reasonable defaults (which you can update). - * @param userAgent to report in your HTTP requests. - * @return AndroidHttpClient for you to use for all your requests. - * - * @deprecated Please use {@link java.net.URLConnection} and friends instead. See - * {@link android.net.SSLCertificateSocketFactory} for SSL cache support. If you'd - * like to set a custom useragent, please use {@link java.net.URLConnection#setRequestProperty(String, String)} - * with {@code field} set to {@code User-Agent}. - */ - @Deprecated - public static AndroidHttpClient newInstance(String userAgent) { - return newInstance(userAgent, null /* session cache */); - } - - private final HttpClient delegate; - - private RuntimeException mLeakedException = new IllegalStateException( - "AndroidHttpClient created and never closed"); - - private AndroidHttpClient(ClientConnectionManager ccm, HttpParams params) { - this.delegate = new DefaultHttpClient(ccm, params) { - @Override - protected BasicHttpProcessor createHttpProcessor() { - // Add interceptor to prevent making requests from main thread. - BasicHttpProcessor processor = super.createHttpProcessor(); - processor.addRequestInterceptor(sThreadCheckInterceptor); - processor.addRequestInterceptor(new CurlLogger()); - - return processor; - } - - @Override - protected HttpContext createHttpContext() { - // Same as DefaultHttpClient.createHttpContext() minus the - // cookie store. - HttpContext context = new BasicHttpContext(); - context.setAttribute( - ClientContext.AUTHSCHEME_REGISTRY, - getAuthSchemes()); - context.setAttribute( - ClientContext.COOKIESPEC_REGISTRY, - getCookieSpecs()); - context.setAttribute( - ClientContext.CREDS_PROVIDER, - getCredentialsProvider()); - return context; - } - }; - } - - @Override - protected void finalize() throws Throwable { - super.finalize(); - if (mLeakedException != null) { - Log.e(TAG, "Leak found", mLeakedException); - mLeakedException = null; - } - } - - /** - * Modifies a request to indicate to the server that we would like a - * gzipped response. (Uses the "Accept-Encoding" HTTP header.) - * @param request the request to modify - * @see #getUngzippedContent - */ - public static void modifyRequestToAcceptGzipResponse(HttpRequest request) { - request.addHeader("Accept-Encoding", "gzip"); - } - - /** - * Gets the input stream from a response entity. If the entity is gzipped - * then this will get a stream over the uncompressed data. - * - * @param entity the entity whose content should be read - * @return the input stream to read from - * @throws IOException - */ - public static InputStream getUngzippedContent(HttpEntity entity) - throws IOException { - InputStream responseStream = entity.getContent(); - if (responseStream == null) return responseStream; - Header header = entity.getContentEncoding(); - if (header == null) return responseStream; - String contentEncoding = header.getValue(); - if (contentEncoding == null) return responseStream; - if (contentEncoding.contains("gzip")) responseStream - = new GZIPInputStream(responseStream); - return responseStream; - } - - /** - * Release resources associated with this client. You must call this, - * or significant resources (sockets and memory) may be leaked. - */ - public void close() { - if (mLeakedException != null) { - getConnectionManager().shutdown(); - mLeakedException = null; - } - } - - public HttpParams getParams() { - return delegate.getParams(); - } - - public ClientConnectionManager getConnectionManager() { - return delegate.getConnectionManager(); - } - - public HttpResponse execute(HttpUriRequest request) throws IOException { - return delegate.execute(request); - } - - public HttpResponse execute(HttpUriRequest request, HttpContext context) - throws IOException { - return delegate.execute(request, context); - } - - public HttpResponse execute(HttpHost target, HttpRequest request) - throws IOException { - return delegate.execute(target, request); - } - - public HttpResponse execute(HttpHost target, HttpRequest request, - HttpContext context) throws IOException { - return delegate.execute(target, request, context); - } - - public <T> T execute(HttpUriRequest request, - ResponseHandler<? extends T> responseHandler) - throws IOException, ClientProtocolException { - return delegate.execute(request, responseHandler); - } - - public <T> T execute(HttpUriRequest request, - ResponseHandler<? extends T> responseHandler, HttpContext context) - throws IOException, ClientProtocolException { - return delegate.execute(request, responseHandler, context); - } - - public <T> T execute(HttpHost target, HttpRequest request, - ResponseHandler<? extends T> responseHandler) throws IOException, - ClientProtocolException { - return delegate.execute(target, request, responseHandler); - } - - public <T> T execute(HttpHost target, HttpRequest request, - ResponseHandler<? extends T> responseHandler, HttpContext context) - throws IOException, ClientProtocolException { - return delegate.execute(target, request, responseHandler, context); - } - - /** - * Compress data to send to server. - * Creates a Http Entity holding the gzipped data. - * The data will not be compressed if it is too short. - * @param data The bytes to compress - * @return Entity holding the data - */ - public static AbstractHttpEntity getCompressedEntity(byte data[], ContentResolver resolver) - throws IOException { - AbstractHttpEntity entity; - if (data.length < getMinGzipSize(resolver)) { - entity = new ByteArrayEntity(data); - } else { - ByteArrayOutputStream arr = new ByteArrayOutputStream(); - OutputStream zipper = new GZIPOutputStream(arr); - zipper.write(data); - zipper.close(); - entity = new ByteArrayEntity(arr.toByteArray()); - entity.setContentEncoding("gzip"); - } - return entity; - } - - /** - * Retrieves the minimum size for compressing data. - * Shorter data will not be compressed. - */ - public static long getMinGzipSize(ContentResolver resolver) { - return DEFAULT_SYNC_MIN_GZIP_BYTES; // For now, this is just a constant. - } - - /* cURL logging support. */ - - /** - * Logging tag and level. - */ - private static class LoggingConfiguration { - - private final String tag; - private final int level; - - private LoggingConfiguration(String tag, int level) { - this.tag = tag; - this.level = level; - } - - /** - * Returns true if logging is turned on for this configuration. - */ - private boolean isLoggable() { - return Log.isLoggable(tag, level); - } - - /** - * Prints a message using this configuration. - */ - private void println(String message) { - Log.println(level, tag, message); - } - } - - /** cURL logging configuration. */ - private volatile LoggingConfiguration curlConfiguration; - - /** - * Enables cURL request logging for this client. - * - * @param name to log messages with - * @param level at which to log messages (see {@link android.util.Log}) - */ - public void enableCurlLogging(String name, int level) { - if (name == null) { - throw new NullPointerException("name"); - } - if (level < Log.VERBOSE || level > Log.ASSERT) { - throw new IllegalArgumentException("Level is out of range [" - + Log.VERBOSE + ".." + Log.ASSERT + "]"); - } - - curlConfiguration = new LoggingConfiguration(name, level); - } - - /** - * Disables cURL logging for this client. - */ - public void disableCurlLogging() { - curlConfiguration = null; - } - - /** - * Logs cURL commands equivalent to requests. - */ - private class CurlLogger implements HttpRequestInterceptor { - public void process(HttpRequest request, HttpContext context) - throws HttpException, IOException { - LoggingConfiguration configuration = curlConfiguration; - if (configuration != null - && configuration.isLoggable() - && request instanceof HttpUriRequest) { - // Never print auth token -- we used to check ro.secure=0 to - // enable that, but can't do that in unbundled code. - configuration.println(toCurl((HttpUriRequest) request, false)); - } - } - } - - /** - * Generates a cURL command equivalent to the given request. - */ - private static String toCurl(HttpUriRequest request, boolean logAuthToken) throws IOException { - StringBuilder builder = new StringBuilder(); - - builder.append("curl "); - - // add in the method - builder.append("-X "); - builder.append(request.getMethod()); - builder.append(" "); - - for (Header header: request.getAllHeaders()) { - if (!logAuthToken - && (header.getName().equals("Authorization") || - header.getName().equals("Cookie"))) { - continue; - } - builder.append("--header \""); - builder.append(header.toString().trim()); - builder.append("\" "); - } - - URI uri = request.getURI(); - - // If this is a wrapped request, use the URI from the original - // request instead. getURI() on the wrapper seems to return a - // relative URI. We want an absolute URI. - if (request instanceof RequestWrapper) { - HttpRequest original = ((RequestWrapper) request).getOriginal(); - if (original instanceof HttpUriRequest) { - uri = ((HttpUriRequest) original).getURI(); - } - } - - builder.append("\""); - builder.append(uri); - builder.append("\""); - - if (request instanceof HttpEntityEnclosingRequest) { - HttpEntityEnclosingRequest entityRequest = - (HttpEntityEnclosingRequest) request; - HttpEntity entity = entityRequest.getEntity(); - if (entity != null && entity.isRepeatable()) { - if (entity.getContentLength() < 1024) { - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - entity.writeTo(stream); - - if (isBinaryContent(request)) { - String base64 = Base64.encodeToString(stream.toByteArray(), Base64.NO_WRAP); - builder.insert(0, "echo '" + base64 + "' | base64 -d > /tmp/$$.bin; "); - builder.append(" --data-binary @/tmp/$$.bin"); - } else { - String entityString = stream.toString(); - builder.append(" --data-ascii \"") - .append(entityString) - .append("\""); - } - } else { - builder.append(" [TOO MUCH DATA TO INCLUDE]"); - } - } - } - - return builder.toString(); - } - - private static boolean isBinaryContent(HttpUriRequest request) { - Header[] headers; - headers = request.getHeaders(Headers.CONTENT_ENCODING); - if (headers != null) { - for (Header header : headers) { - if ("gzip".equalsIgnoreCase(header.getValue())) { - return true; - } - } - } - - headers = request.getHeaders(Headers.CONTENT_TYPE); - if (headers != null) { - for (Header header : headers) { - for (String contentType : textContentTypes) { - if (header.getValue().startsWith(contentType)) { - return false; - } - } - } - } - return true; - } - - /** - * Returns the date of the given HTTP date string. This method can identify - * and parse the date formats emitted by common HTTP servers, such as - * <a href="http://www.ietf.org/rfc/rfc0822.txt">RFC 822</a>, - * <a href="http://www.ietf.org/rfc/rfc0850.txt">RFC 850</a>, - * <a href="http://www.ietf.org/rfc/rfc1036.txt">RFC 1036</a>, - * <a href="http://www.ietf.org/rfc/rfc1123.txt">RFC 1123</a> and - * <a href="http://www.opengroup.org/onlinepubs/007908799/xsh/asctime.html">ANSI - * C's asctime()</a>. - * - * @return the number of milliseconds since Jan. 1, 1970, midnight GMT. - * @throws IllegalArgumentException if {@code dateString} is not a date or - * of an unsupported format. - */ - public static long parseDate(String dateString) { - return HttpDateTime.parse(dateString); - } -} diff --git a/core/java/android/net/http/AndroidHttpClientConnection.java b/core/java/android/net/http/AndroidHttpClientConnection.java deleted file mode 100644 index 6d48fce..0000000 --- a/core/java/android/net/http/AndroidHttpClientConnection.java +++ /dev/null @@ -1,460 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.http; - -import org.apache.http.HttpConnection; -import org.apache.http.HttpClientConnection; -import org.apache.http.HttpConnectionMetrics; -import org.apache.http.HttpEntity; -import org.apache.http.HttpEntityEnclosingRequest; -import org.apache.http.HttpException; -import org.apache.http.HttpInetConnection; -import org.apache.http.HttpRequest; -import org.apache.http.HttpResponse; -import org.apache.http.NoHttpResponseException; -import org.apache.http.StatusLine; -import org.apache.http.entity.BasicHttpEntity; -import org.apache.http.entity.ContentLengthStrategy; -import org.apache.http.impl.HttpConnectionMetricsImpl; -import org.apache.http.impl.entity.EntitySerializer; -import org.apache.http.impl.entity.StrictContentLengthStrategy; -import org.apache.http.impl.io.ChunkedInputStream; -import org.apache.http.impl.io.ContentLengthInputStream; -import org.apache.http.impl.io.HttpRequestWriter; -import org.apache.http.impl.io.IdentityInputStream; -import org.apache.http.impl.io.SocketInputBuffer; -import org.apache.http.impl.io.SocketOutputBuffer; -import org.apache.http.io.HttpMessageWriter; -import org.apache.http.io.SessionInputBuffer; -import org.apache.http.io.SessionOutputBuffer; -import org.apache.http.message.BasicLineParser; -import org.apache.http.message.ParserCursor; -import org.apache.http.params.CoreConnectionPNames; -import org.apache.http.params.HttpConnectionParams; -import org.apache.http.params.HttpParams; -import org.apache.http.ParseException; -import org.apache.http.util.CharArrayBuffer; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.Socket; -import java.net.SocketException; - -/** - * A alternate class for (@link DefaultHttpClientConnection). - * It has better performance than DefaultHttpClientConnection - * - * {@hide} - */ -public class AndroidHttpClientConnection - implements HttpInetConnection, HttpConnection { - - private SessionInputBuffer inbuffer = null; - private SessionOutputBuffer outbuffer = null; - private int maxHeaderCount; - // store CoreConnectionPNames.MAX_LINE_LENGTH for performance - private int maxLineLength; - - private final EntitySerializer entityserializer; - - private HttpMessageWriter requestWriter = null; - private HttpConnectionMetricsImpl metrics = null; - private volatile boolean open; - private Socket socket = null; - - public AndroidHttpClientConnection() { - this.entityserializer = new EntitySerializer( - new StrictContentLengthStrategy()); - } - - /** - * Bind socket and set HttpParams to AndroidHttpClientConnection - * @param socket outgoing socket - * @param params HttpParams - * @throws IOException - */ - public void bind( - final Socket socket, - final HttpParams params) throws IOException { - if (socket == null) { - throw new IllegalArgumentException("Socket may not be null"); - } - if (params == null) { - throw new IllegalArgumentException("HTTP parameters may not be null"); - } - assertNotOpen(); - socket.setTcpNoDelay(HttpConnectionParams.getTcpNoDelay(params)); - socket.setSoTimeout(HttpConnectionParams.getSoTimeout(params)); - - int linger = HttpConnectionParams.getLinger(params); - if (linger >= 0) { - socket.setSoLinger(linger > 0, linger); - } - this.socket = socket; - - int buffersize = HttpConnectionParams.getSocketBufferSize(params); - this.inbuffer = new SocketInputBuffer(socket, buffersize, params); - this.outbuffer = new SocketOutputBuffer(socket, buffersize, params); - - maxHeaderCount = params.getIntParameter( - CoreConnectionPNames.MAX_HEADER_COUNT, -1); - maxLineLength = params.getIntParameter( - CoreConnectionPNames.MAX_LINE_LENGTH, -1); - - this.requestWriter = new HttpRequestWriter(outbuffer, null, params); - - this.metrics = new HttpConnectionMetricsImpl( - inbuffer.getMetrics(), - outbuffer.getMetrics()); - - this.open = true; - } - - @Override - public String toString() { - StringBuilder buffer = new StringBuilder(); - buffer.append(getClass().getSimpleName()).append("["); - if (isOpen()) { - buffer.append(getRemotePort()); - } else { - buffer.append("closed"); - } - buffer.append("]"); - return buffer.toString(); - } - - - private void assertNotOpen() { - if (this.open) { - throw new IllegalStateException("Connection is already open"); - } - } - - private void assertOpen() { - if (!this.open) { - throw new IllegalStateException("Connection is not open"); - } - } - - public boolean isOpen() { - // to make this method useful, we want to check if the socket is connected - return (this.open && this.socket != null && this.socket.isConnected()); - } - - public InetAddress getLocalAddress() { - if (this.socket != null) { - return this.socket.getLocalAddress(); - } else { - return null; - } - } - - public int getLocalPort() { - if (this.socket != null) { - return this.socket.getLocalPort(); - } else { - return -1; - } - } - - public InetAddress getRemoteAddress() { - if (this.socket != null) { - return this.socket.getInetAddress(); - } else { - return null; - } - } - - public int getRemotePort() { - if (this.socket != null) { - return this.socket.getPort(); - } else { - return -1; - } - } - - public void setSocketTimeout(int timeout) { - assertOpen(); - if (this.socket != null) { - try { - this.socket.setSoTimeout(timeout); - } catch (SocketException ignore) { - // It is not quite clear from the original documentation if there are any - // other legitimate cases for a socket exception to be thrown when setting - // SO_TIMEOUT besides the socket being already closed - } - } - } - - public int getSocketTimeout() { - if (this.socket != null) { - try { - return this.socket.getSoTimeout(); - } catch (SocketException ignore) { - return -1; - } - } else { - return -1; - } - } - - public void shutdown() throws IOException { - this.open = false; - Socket tmpsocket = this.socket; - if (tmpsocket != null) { - tmpsocket.close(); - } - } - - public void close() throws IOException { - if (!this.open) { - return; - } - this.open = false; - doFlush(); - try { - try { - this.socket.shutdownOutput(); - } catch (IOException ignore) { - } - try { - this.socket.shutdownInput(); - } catch (IOException ignore) { - } - } catch (UnsupportedOperationException ignore) { - // if one isn't supported, the other one isn't either - } - this.socket.close(); - } - - /** - * Sends the request line and all headers over the connection. - * @param request the request whose headers to send. - * @throws HttpException - * @throws IOException - */ - public void sendRequestHeader(final HttpRequest request) - throws HttpException, IOException { - if (request == null) { - throw new IllegalArgumentException("HTTP request may not be null"); - } - assertOpen(); - this.requestWriter.write(request); - this.metrics.incrementRequestCount(); - } - - /** - * Sends the request entity over the connection. - * @param request the request whose entity to send. - * @throws HttpException - * @throws IOException - */ - public void sendRequestEntity(final HttpEntityEnclosingRequest request) - throws HttpException, IOException { - if (request == null) { - throw new IllegalArgumentException("HTTP request may not be null"); - } - assertOpen(); - if (request.getEntity() == null) { - return; - } - this.entityserializer.serialize( - this.outbuffer, - request, - request.getEntity()); - } - - protected void doFlush() throws IOException { - this.outbuffer.flush(); - } - - public void flush() throws IOException { - assertOpen(); - doFlush(); - } - - /** - * Parses the response headers and adds them to the - * given {@code headers} object, and returns the response StatusLine - * @param headers store parsed header to headers. - * @throws IOException - * @return StatusLine - * @see HttpClientConnection#receiveResponseHeader() - */ - public StatusLine parseResponseHeader(Headers headers) - throws IOException, ParseException { - assertOpen(); - - CharArrayBuffer current = new CharArrayBuffer(64); - - if (inbuffer.readLine(current) == -1) { - throw new NoHttpResponseException("The target server failed to respond"); - } - - // Create the status line from the status string - StatusLine statusline = BasicLineParser.DEFAULT.parseStatusLine( - current, new ParserCursor(0, current.length())); - - if (HttpLog.LOGV) HttpLog.v("read: " + statusline); - int statusCode = statusline.getStatusCode(); - - // Parse header body - CharArrayBuffer previous = null; - int headerNumber = 0; - while(true) { - if (current == null) { - current = new CharArrayBuffer(64); - } else { - // This must be he buffer used to parse the status - current.clear(); - } - int l = inbuffer.readLine(current); - if (l == -1 || current.length() < 1) { - break; - } - // Parse the header name and value - // Check for folded headers first - // Detect LWS-char see HTTP/1.0 or HTTP/1.1 Section 2.2 - // discussion on folded headers - char first = current.charAt(0); - if ((first == ' ' || first == '\t') && previous != null) { - // we have continuation folded header - // so append value - int start = 0; - int length = current.length(); - while (start < length) { - char ch = current.charAt(start); - if (ch != ' ' && ch != '\t') { - break; - } - start++; - } - if (maxLineLength > 0 && - previous.length() + 1 + current.length() - start > - maxLineLength) { - throw new IOException("Maximum line length limit exceeded"); - } - previous.append(' '); - previous.append(current, start, current.length() - start); - } else { - if (previous != null) { - headers.parseHeader(previous); - } - headerNumber++; - previous = current; - current = null; - } - if (maxHeaderCount > 0 && headerNumber >= maxHeaderCount) { - throw new IOException("Maximum header count exceeded"); - } - } - - if (previous != null) { - headers.parseHeader(previous); - } - - if (statusCode >= 200) { - this.metrics.incrementResponseCount(); - } - return statusline; - } - - /** - * Return the next response entity. - * @param headers contains values for parsing entity - * @see HttpClientConnection#receiveResponseEntity(HttpResponse response) - */ - public HttpEntity receiveResponseEntity(final Headers headers) { - assertOpen(); - BasicHttpEntity entity = new BasicHttpEntity(); - - long len = determineLength(headers); - if (len == ContentLengthStrategy.CHUNKED) { - entity.setChunked(true); - entity.setContentLength(-1); - entity.setContent(new ChunkedInputStream(inbuffer)); - } else if (len == ContentLengthStrategy.IDENTITY) { - entity.setChunked(false); - entity.setContentLength(-1); - entity.setContent(new IdentityInputStream(inbuffer)); - } else { - entity.setChunked(false); - entity.setContentLength(len); - entity.setContent(new ContentLengthInputStream(inbuffer, len)); - } - - String contentTypeHeader = headers.getContentType(); - if (contentTypeHeader != null) { - entity.setContentType(contentTypeHeader); - } - String contentEncodingHeader = headers.getContentEncoding(); - if (contentEncodingHeader != null) { - entity.setContentEncoding(contentEncodingHeader); - } - - return entity; - } - - private long determineLength(final Headers headers) { - long transferEncoding = headers.getTransferEncoding(); - // We use Transfer-Encoding if present and ignore Content-Length. - // RFC2616, 4.4 item number 3 - if (transferEncoding < Headers.NO_TRANSFER_ENCODING) { - return transferEncoding; - } else { - long contentlen = headers.getContentLength(); - if (contentlen > Headers.NO_CONTENT_LENGTH) { - return contentlen; - } else { - return ContentLengthStrategy.IDENTITY; - } - } - } - - /** - * Checks whether this connection has gone down. - * Network connections may get closed during some time of inactivity - * for several reasons. The next time a read is attempted on such a - * connection it will throw an IOException. - * This method tries to alleviate this inconvenience by trying to - * find out if a connection is still usable. Implementations may do - * that by attempting a read with a very small timeout. Thus this - * method may block for a small amount of time before returning a result. - * It is therefore an <i>expensive</i> operation. - * - * @return <code>true</code> if attempts to use this connection are - * likely to succeed, or <code>false</code> if they are likely - * to fail and this connection should be closed - */ - public boolean isStale() { - assertOpen(); - try { - this.inbuffer.isDataAvailable(1); - return false; - } catch (IOException ex) { - return true; - } - } - - /** - * Returns a collection of connection metrcis - * @return HttpConnectionMetrics - */ - public HttpConnectionMetrics getMetrics() { - return this.metrics; - } -} diff --git a/core/java/android/net/http/CertificateChainValidator.java b/core/java/android/net/http/CertificateChainValidator.java deleted file mode 100644 index bf3fe02..0000000 --- a/core/java/android/net/http/CertificateChainValidator.java +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.http; - -import com.android.org.conscrypt.SSLParametersImpl; -import com.android.org.conscrypt.TrustManagerImpl; - -import android.util.Slog; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.lang.reflect.Method; -import java.security.GeneralSecurityException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.Certificate; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; - -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLHandshakeException; -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509TrustManager; - -/** - * Class responsible for all server certificate validation functionality - * - * {@hide} - */ -public class CertificateChainValidator { - private static final String TAG = "CertificateChainValidator"; - - private static class NoPreloadHolder { - /** - * The singleton instance of the certificate chain validator. - */ - private static final CertificateChainValidator sInstance = new CertificateChainValidator(); - - /** - * The singleton instance of the hostname verifier. - */ - private static final HostnameVerifier sVerifier = HttpsURLConnection - .getDefaultHostnameVerifier(); - } - - private X509TrustManager mTrustManager; - - /** - * @return The singleton instance of the certificates chain validator - */ - public static CertificateChainValidator getInstance() { - return NoPreloadHolder.sInstance; - } - - /** - * Creates a new certificate chain validator. This is a private constructor. - * If you need a Certificate chain validator, call getInstance(). - */ - private CertificateChainValidator() { - try { - TrustManagerFactory tmf = TrustManagerFactory.getInstance("X.509"); - tmf.init((KeyStore) null); - for (TrustManager tm : tmf.getTrustManagers()) { - if (tm instanceof X509TrustManager) { - mTrustManager = (X509TrustManager) tm; - } - } - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("X.509 TrustManagerFactory must be available", e); - } catch (KeyStoreException e) { - throw new RuntimeException("X.509 TrustManagerFactory cannot be initialized", e); - } - - if (mTrustManager == null) { - throw new RuntimeException( - "None of the X.509 TrustManagers are X509TrustManager"); - } - } - - /** - * Performs the handshake and server certificates validation - * Notice a new chain will be rebuilt by tracing the issuer and subject - * before calling checkServerTrusted(). - * And if the last traced certificate is self issued and it is expired, it - * will be dropped. - * @param sslSocket The secure connection socket - * @param domain The website domain - * @return An SSL error object if there is an error and null otherwise - */ - public SslError doHandshakeAndValidateServerCertificates( - HttpsConnection connection, SSLSocket sslSocket, String domain) - throws IOException { - // get a valid SSLSession, close the socket if we fail - SSLSession sslSession = sslSocket.getSession(); - if (!sslSession.isValid()) { - closeSocketThrowException(sslSocket, "failed to perform SSL handshake"); - } - - // retrieve the chain of the server peer certificates - Certificate[] peerCertificates = - sslSocket.getSession().getPeerCertificates(); - - if (peerCertificates == null || peerCertificates.length == 0) { - closeSocketThrowException( - sslSocket, "failed to retrieve peer certificates"); - } else { - // update the SSL certificate associated with the connection - if (connection != null) { - if (peerCertificates[0] != null) { - connection.setCertificate( - new SslCertificate((X509Certificate)peerCertificates[0])); - } - } - } - - return verifyServerDomainAndCertificates((X509Certificate[]) peerCertificates, domain, "RSA"); - } - - /** - * Similar to doHandshakeAndValidateServerCertificates but exposed to JNI for use - * by Chromium HTTPS stack to validate the cert chain. - * @param certChain The bytes for certificates in ASN.1 DER encoded certificates format. - * @param domain The full website hostname and domain - * @param authType The authentication type for the cert chain - * @return An SSL error object if there is an error and null otherwise - */ - public static SslError verifyServerCertificates( - byte[][] certChain, String domain, String authType) - throws IOException { - - if (certChain == null || certChain.length == 0) { - throw new IllegalArgumentException("bad certificate chain"); - } - - X509Certificate[] serverCertificates = new X509Certificate[certChain.length]; - - try { - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - for (int i = 0; i < certChain.length; ++i) { - serverCertificates[i] = (X509Certificate) cf.generateCertificate( - new ByteArrayInputStream(certChain[i])); - } - } catch (CertificateException e) { - throw new IOException("can't read certificate", e); - } - - return verifyServerDomainAndCertificates(serverCertificates, domain, authType); - } - - /** - * Handles updates to credential storage. - */ - public static void handleTrustStorageUpdate() { - TrustManagerFactory tmf; - try { - tmf = TrustManagerFactory.getInstance("X.509"); - tmf.init((KeyStore) null); - } catch (NoSuchAlgorithmException e) { - Slog.w(TAG, "Couldn't find default X.509 TrustManagerFactory"); - return; - } catch (KeyStoreException e) { - Slog.w(TAG, "Couldn't initialize default X.509 TrustManagerFactory", e); - return; - } - - TrustManager[] tms = tmf.getTrustManagers(); - boolean sentUpdate = false; - for (TrustManager tm : tms) { - try { - Method updateMethod = tm.getClass().getDeclaredMethod("handleTrustStorageUpdate"); - updateMethod.setAccessible(true); - updateMethod.invoke(tm); - sentUpdate = true; - } catch (Exception e) { - } - } - if (!sentUpdate) { - Slog.w(TAG, "Didn't find a TrustManager to handle CA list update"); - } - } - - /** - * Common code of doHandshakeAndValidateServerCertificates and verifyServerCertificates. - * Calls DomainNamevalidator to verify the domain, and TrustManager to verify the certs. - * @param chain the cert chain in X509 cert format. - * @param domain The full website hostname and domain - * @param authType The authentication type for the cert chain - * @return An SSL error object if there is an error and null otherwise - */ - private static SslError verifyServerDomainAndCertificates( - X509Certificate[] chain, String domain, String authType) - throws IOException { - // check if the first certificate in the chain is for this site - X509Certificate currCertificate = chain[0]; - if (currCertificate == null) { - throw new IllegalArgumentException("certificate for this site is null"); - } - - boolean valid = domain != null - && !domain.isEmpty() - && NoPreloadHolder.sVerifier.verify(domain, - new DelegatingSSLSession.CertificateWrap(currCertificate)); - if (!valid) { - if (HttpLog.LOGV) { - HttpLog.v("certificate not for this host: " + domain); - } - return new SslError(SslError.SSL_IDMISMATCH, currCertificate); - } - - try { - X509TrustManager x509TrustManager = SSLParametersImpl.getDefaultX509TrustManager(); - if (x509TrustManager instanceof TrustManagerImpl) { - TrustManagerImpl trustManager = (TrustManagerImpl) x509TrustManager; - trustManager.checkServerTrusted(chain, authType, domain); - } else { - x509TrustManager.checkServerTrusted(chain, authType); - } - return null; // No errors. - } catch (GeneralSecurityException e) { - if (HttpLog.LOGV) { - HttpLog.v("failed to validate the certificate chain, error: " + - e.getMessage()); - } - return new SslError(SslError.SSL_UNTRUSTED, currCertificate); - } - } - - /** - * Returns the platform default {@link X509TrustManager}. - */ - private X509TrustManager getTrustManager() { - return mTrustManager; - } - - private void closeSocketThrowException( - SSLSocket socket, String errorMessage, String defaultErrorMessage) - throws IOException { - closeSocketThrowException( - socket, errorMessage != null ? errorMessage : defaultErrorMessage); - } - - private void closeSocketThrowException(SSLSocket socket, - String errorMessage) throws IOException { - if (HttpLog.LOGV) { - HttpLog.v("validation error: " + errorMessage); - } - - if (socket != null) { - SSLSession session = socket.getSession(); - if (session != null) { - session.invalidate(); - } - - socket.close(); - } - - throw new SSLHandshakeException(errorMessage); - } -} diff --git a/core/java/android/net/http/Connection.java b/core/java/android/net/http/Connection.java deleted file mode 100644 index 831bd0e..0000000 --- a/core/java/android/net/http/Connection.java +++ /dev/null @@ -1,575 +0,0 @@ -/* - * Copyright (C) 2007 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.net.http; - -import android.content.Context; -import android.os.SystemClock; - -import java.io.IOException; -import java.net.UnknownHostException; -import java.util.LinkedList; - -import javax.net.ssl.SSLHandshakeException; - -import org.apache.http.ConnectionReuseStrategy; -import org.apache.http.HttpEntity; -import org.apache.http.HttpException; -import org.apache.http.HttpHost; -import org.apache.http.HttpVersion; -import org.apache.http.ParseException; -import org.apache.http.ProtocolVersion; -import org.apache.http.protocol.ExecutionContext; -import org.apache.http.protocol.HttpContext; -import org.apache.http.protocol.BasicHttpContext; - -/** - * {@hide} - */ -abstract class Connection { - - /** - * Allow a TCP connection 60 idle seconds before erroring out - */ - static final int SOCKET_TIMEOUT = 60000; - - private static final int SEND = 0; - private static final int READ = 1; - private static final int DRAIN = 2; - private static final int DONE = 3; - private static final String[] states = {"SEND", "READ", "DRAIN", "DONE"}; - - Context mContext; - - /** The low level connection */ - protected AndroidHttpClientConnection mHttpClientConnection = null; - - /** - * The server SSL certificate associated with this connection - * (null if the connection is not secure) - * It would be nice to store the whole certificate chain, but - * we want to keep things as light-weight as possible - */ - protected SslCertificate mCertificate = null; - - /** - * The host this connection is connected to. If using proxy, - * this is set to the proxy address - */ - HttpHost mHost; - - /** true if the connection can be reused for sending more requests */ - private boolean mCanPersist; - - /** context required by ConnectionReuseStrategy. */ - private HttpContext mHttpContext; - - /** set when cancelled */ - private static int STATE_NORMAL = 0; - private static int STATE_CANCEL_REQUESTED = 1; - private int mActive = STATE_NORMAL; - - /** The number of times to try to re-connect (if connect fails). */ - private final static int RETRY_REQUEST_LIMIT = 2; - - private static final int MIN_PIPE = 2; - private static final int MAX_PIPE = 3; - - /** - * Doesn't seem to exist anymore in the new HTTP client, so copied here. - */ - private static final String HTTP_CONNECTION = "http.connection"; - - RequestFeeder mRequestFeeder; - - /** - * Buffer for feeding response blocks to webkit. One block per - * connection reduces memory churn. - */ - private byte[] mBuf; - - protected Connection(Context context, HttpHost host, - RequestFeeder requestFeeder) { - mContext = context; - mHost = host; - mRequestFeeder = requestFeeder; - - mCanPersist = false; - mHttpContext = new BasicHttpContext(null); - } - - HttpHost getHost() { - return mHost; - } - - /** - * connection factory: returns an HTTP or HTTPS connection as - * necessary - */ - static Connection getConnection( - Context context, HttpHost host, HttpHost proxy, - RequestFeeder requestFeeder) { - - if (host.getSchemeName().equals("http")) { - return new HttpConnection(context, host, requestFeeder); - } - - // Otherwise, default to https - return new HttpsConnection(context, host, proxy, requestFeeder); - } - - /** - * @return The server SSL certificate associated with this - * connection (null if the connection is not secure) - */ - /* package */ SslCertificate getCertificate() { - return mCertificate; - } - - /** - * Close current network connection - * Note: this runs in non-network thread - */ - void cancel() { - mActive = STATE_CANCEL_REQUESTED; - closeConnection(); - if (HttpLog.LOGV) HttpLog.v( - "Connection.cancel(): connection closed " + mHost); - } - - /** - * Process requests in queue - * pipelines requests - */ - void processRequests(Request firstRequest) { - Request req = null; - boolean empty; - int error = EventHandler.OK; - Exception exception = null; - - LinkedList<Request> pipe = new LinkedList<Request>(); - - int minPipe = MIN_PIPE, maxPipe = MAX_PIPE; - int state = SEND; - - while (state != DONE) { - if (HttpLog.LOGV) HttpLog.v( - states[state] + " pipe " + pipe.size()); - - /* If a request was cancelled, give other cancel requests - some time to go through so we don't uselessly restart - connections */ - if (mActive == STATE_CANCEL_REQUESTED) { - try { - Thread.sleep(100); - } catch (InterruptedException x) { /* ignore */ } - mActive = STATE_NORMAL; - } - - switch (state) { - case SEND: { - if (pipe.size() == maxPipe) { - state = READ; - break; - } - /* get a request */ - if (firstRequest == null) { - req = mRequestFeeder.getRequest(mHost); - } else { - req = firstRequest; - firstRequest = null; - } - if (req == null) { - state = DRAIN; - break; - } - req.setConnection(this); - - /* Don't work on cancelled requests. */ - if (req.mCancelled) { - if (HttpLog.LOGV) HttpLog.v( - "processRequests(): skipping cancelled request " - + req); - req.complete(); - break; - } - - if (mHttpClientConnection == null || - !mHttpClientConnection.isOpen()) { - /* If this call fails, the address is bad or - the net is down. Punt for now. - - FIXME: blow out entire queue here on - connection failure if net up? */ - - if (!openHttpConnection(req)) { - state = DONE; - break; - } - } - - /* we have a connection, let the event handler - * know of any associated certificate, - * potentially none. - */ - req.mEventHandler.certificate(mCertificate); - - try { - /* FIXME: don't increment failure count if old - connection? There should not be a penalty for - attempting to reuse an old connection */ - req.sendRequest(mHttpClientConnection); - } catch (HttpException e) { - exception = e; - error = EventHandler.ERROR; - } catch (IOException e) { - exception = e; - error = EventHandler.ERROR_IO; - } catch (IllegalStateException e) { - exception = e; - error = EventHandler.ERROR_IO; - } - if (exception != null) { - if (httpFailure(req, error, exception) && - !req.mCancelled) { - /* retry request if not permanent failure - or cancelled */ - pipe.addLast(req); - } - exception = null; - state = clearPipe(pipe) ? DONE : SEND; - minPipe = maxPipe = 1; - break; - } - - pipe.addLast(req); - if (!mCanPersist) state = READ; - break; - - } - case DRAIN: - case READ: { - empty = !mRequestFeeder.haveRequest(mHost); - int pipeSize = pipe.size(); - if (state != DRAIN && pipeSize < minPipe && - !empty && mCanPersist) { - state = SEND; - break; - } else if (pipeSize == 0) { - /* Done if no other work to do */ - state = empty ? DONE : SEND; - break; - } - - req = (Request)pipe.removeFirst(); - if (HttpLog.LOGV) HttpLog.v( - "processRequests() reading " + req); - - try { - req.readResponse(mHttpClientConnection); - } catch (ParseException e) { - exception = e; - error = EventHandler.ERROR_IO; - } catch (IOException e) { - exception = e; - error = EventHandler.ERROR_IO; - } catch (IllegalStateException e) { - exception = e; - error = EventHandler.ERROR_IO; - } - if (exception != null) { - if (httpFailure(req, error, exception) && - !req.mCancelled) { - /* retry request if not permanent failure - or cancelled */ - req.reset(); - pipe.addFirst(req); - } - exception = null; - mCanPersist = false; - } - if (!mCanPersist) { - if (HttpLog.LOGV) HttpLog.v( - "processRequests(): no persist, closing " + - mHost); - - closeConnection(); - - mHttpContext.removeAttribute(HTTP_CONNECTION); - clearPipe(pipe); - minPipe = maxPipe = 1; - state = SEND; - } - break; - } - } - } - } - - /** - * After a send/receive failure, any pipelined requests must be - * cleared back to the mRequest queue - * @return true if mRequests is empty after pipe cleared - */ - private boolean clearPipe(LinkedList<Request> pipe) { - boolean empty = true; - if (HttpLog.LOGV) HttpLog.v( - "Connection.clearPipe(): clearing pipe " + pipe.size()); - synchronized (mRequestFeeder) { - Request tReq; - while (!pipe.isEmpty()) { - tReq = (Request)pipe.removeLast(); - if (HttpLog.LOGV) HttpLog.v( - "clearPipe() adding back " + mHost + " " + tReq); - mRequestFeeder.requeueRequest(tReq); - empty = false; - } - if (empty) empty = !mRequestFeeder.haveRequest(mHost); - } - return empty; - } - - /** - * @return true on success - */ - private boolean openHttpConnection(Request req) { - - long now = SystemClock.uptimeMillis(); - int error = EventHandler.OK; - Exception exception = null; - - try { - // reset the certificate to null before opening a connection - mCertificate = null; - mHttpClientConnection = openConnection(req); - if (mHttpClientConnection != null) { - mHttpClientConnection.setSocketTimeout(SOCKET_TIMEOUT); - mHttpContext.setAttribute(HTTP_CONNECTION, - mHttpClientConnection); - } else { - // we tried to do SSL tunneling, failed, - // and need to drop the request; - // we have already informed the handler - req.mFailCount = RETRY_REQUEST_LIMIT; - return false; - } - } catch (UnknownHostException e) { - if (HttpLog.LOGV) HttpLog.v("Failed to open connection"); - error = EventHandler.ERROR_LOOKUP; - exception = e; - } catch (IllegalArgumentException e) { - if (HttpLog.LOGV) HttpLog.v("Illegal argument exception"); - error = EventHandler.ERROR_CONNECT; - req.mFailCount = RETRY_REQUEST_LIMIT; - exception = e; - } catch (SSLConnectionClosedByUserException e) { - // hack: if we have an SSL connection failure, - // we don't want to reconnect - req.mFailCount = RETRY_REQUEST_LIMIT; - // no error message - return false; - } catch (SSLHandshakeException e) { - // hack: if we have an SSL connection failure, - // we don't want to reconnect - req.mFailCount = RETRY_REQUEST_LIMIT; - if (HttpLog.LOGV) HttpLog.v( - "SSL exception performing handshake"); - error = EventHandler.ERROR_FAILED_SSL_HANDSHAKE; - exception = e; - } catch (IOException e) { - error = EventHandler.ERROR_CONNECT; - exception = e; - } - - if (HttpLog.LOGV) { - long now2 = SystemClock.uptimeMillis(); - HttpLog.v("Connection.openHttpConnection() " + - (now2 - now) + " " + mHost); - } - - if (error == EventHandler.OK) { - return true; - } else { - if (req.mFailCount < RETRY_REQUEST_LIMIT) { - // requeue - mRequestFeeder.requeueRequest(req); - req.mFailCount++; - } else { - httpFailure(req, error, exception); - } - return error == EventHandler.OK; - } - } - - /** - * Helper. Calls the mEventHandler's error() method only if - * request failed permanently. Increments mFailcount on failure. - * - * Increments failcount only if the network is believed to be - * connected - * - * @return true if request can be retried (less than - * RETRY_REQUEST_LIMIT failures have occurred). - */ - private boolean httpFailure(Request req, int errorId, Exception e) { - boolean ret = true; - - // e.printStackTrace(); - if (HttpLog.LOGV) HttpLog.v( - "httpFailure() ******* " + e + " count " + req.mFailCount + - " " + mHost + " " + req.getUri()); - - if (++req.mFailCount >= RETRY_REQUEST_LIMIT) { - ret = false; - String error; - if (errorId < 0) { - error = getEventHandlerErrorString(errorId); - } else { - Throwable cause = e.getCause(); - error = cause != null ? cause.toString() : e.getMessage(); - } - req.mEventHandler.error(errorId, error); - req.complete(); - } - - closeConnection(); - mHttpContext.removeAttribute(HTTP_CONNECTION); - - return ret; - } - - private static String getEventHandlerErrorString(int errorId) { - switch (errorId) { - case EventHandler.OK: - return "OK"; - - case EventHandler.ERROR: - return "ERROR"; - - case EventHandler.ERROR_LOOKUP: - return "ERROR_LOOKUP"; - - case EventHandler.ERROR_UNSUPPORTED_AUTH_SCHEME: - return "ERROR_UNSUPPORTED_AUTH_SCHEME"; - - case EventHandler.ERROR_AUTH: - return "ERROR_AUTH"; - - case EventHandler.ERROR_PROXYAUTH: - return "ERROR_PROXYAUTH"; - - case EventHandler.ERROR_CONNECT: - return "ERROR_CONNECT"; - - case EventHandler.ERROR_IO: - return "ERROR_IO"; - - case EventHandler.ERROR_TIMEOUT: - return "ERROR_TIMEOUT"; - - case EventHandler.ERROR_REDIRECT_LOOP: - return "ERROR_REDIRECT_LOOP"; - - case EventHandler.ERROR_UNSUPPORTED_SCHEME: - return "ERROR_UNSUPPORTED_SCHEME"; - - case EventHandler.ERROR_FAILED_SSL_HANDSHAKE: - return "ERROR_FAILED_SSL_HANDSHAKE"; - - case EventHandler.ERROR_BAD_URL: - return "ERROR_BAD_URL"; - - case EventHandler.FILE_ERROR: - return "FILE_ERROR"; - - case EventHandler.FILE_NOT_FOUND_ERROR: - return "FILE_NOT_FOUND_ERROR"; - - case EventHandler.TOO_MANY_REQUESTS_ERROR: - return "TOO_MANY_REQUESTS_ERROR"; - - default: - return "UNKNOWN_ERROR"; - } - } - - HttpContext getHttpContext() { - return mHttpContext; - } - - /** - * Use same logic as ConnectionReuseStrategy - * @see ConnectionReuseStrategy - */ - private boolean keepAlive(HttpEntity entity, - ProtocolVersion ver, int connType, final HttpContext context) { - org.apache.http.HttpConnection conn = (org.apache.http.HttpConnection) - context.getAttribute(ExecutionContext.HTTP_CONNECTION); - - if (conn != null && !conn.isOpen()) - return false; - // do NOT check for stale connection, that is an expensive operation - - if (entity != null) { - if (entity.getContentLength() < 0) { - if (!entity.isChunked() || ver.lessEquals(HttpVersion.HTTP_1_0)) { - // if the content length is not known and is not chunk - // encoded, the connection cannot be reused - return false; - } - } - } - // Check for 'Connection' directive - if (connType == Headers.CONN_CLOSE) { - return false; - } else if (connType == Headers.CONN_KEEP_ALIVE) { - return true; - } - // Resorting to protocol version default close connection policy - return !ver.lessEquals(HttpVersion.HTTP_1_0); - } - - void setCanPersist(HttpEntity entity, ProtocolVersion ver, int connType) { - mCanPersist = keepAlive(entity, ver, connType, mHttpContext); - } - - void setCanPersist(boolean canPersist) { - mCanPersist = canPersist; - } - - boolean getCanPersist() { - return mCanPersist; - } - - /** typically http or https... set by subclass */ - abstract String getScheme(); - abstract void closeConnection(); - abstract AndroidHttpClientConnection openConnection(Request req) throws IOException; - - /** - * Prints request queue to log, for debugging. - * returns request count - */ - public synchronized String toString() { - return mHost.toString(); - } - - byte[] getBuf() { - if (mBuf == null) mBuf = new byte[8192]; - return mBuf; - } - -} diff --git a/core/java/android/net/http/ConnectionThread.java b/core/java/android/net/http/ConnectionThread.java deleted file mode 100644 index d825530..0000000 --- a/core/java/android/net/http/ConnectionThread.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.http; - -import android.content.Context; -import android.os.SystemClock; - -import java.lang.Thread; - -/** - * {@hide} - */ -class ConnectionThread extends Thread { - - static final int WAIT_TIMEOUT = 5000; - static final int WAIT_TICK = 1000; - - // Performance probe - long mCurrentThreadTime; - long mTotalThreadTime; - - private boolean mWaiting; - private volatile boolean mRunning = true; - private Context mContext; - private RequestQueue.ConnectionManager mConnectionManager; - private RequestFeeder mRequestFeeder; - - private int mId; - Connection mConnection; - - ConnectionThread(Context context, - int id, - RequestQueue.ConnectionManager connectionManager, - RequestFeeder requestFeeder) { - super(); - mContext = context; - setName("http" + id); - mId = id; - mConnectionManager = connectionManager; - mRequestFeeder = requestFeeder; - } - - void requestStop() { - synchronized (mRequestFeeder) { - mRunning = false; - mRequestFeeder.notify(); - } - } - - /** - * Loop until app shutdown. Runs connections in priority - * order. - */ - public void run() { - android.os.Process.setThreadPriority( - android.os.Process.THREAD_PRIORITY_DEFAULT + - android.os.Process.THREAD_PRIORITY_LESS_FAVORABLE); - - // these are used to get performance data. When it is not in the timing, - // mCurrentThreadTime is 0. When it starts timing, mCurrentThreadTime is - // first set to -1, it will be set to the current thread time when the - // next request starts. - mCurrentThreadTime = 0; - mTotalThreadTime = 0; - - while (mRunning) { - if (mCurrentThreadTime == -1) { - mCurrentThreadTime = SystemClock.currentThreadTimeMillis(); - } - - Request request; - - /* Get a request to process */ - request = mRequestFeeder.getRequest(); - - /* wait for work */ - if (request == null) { - synchronized(mRequestFeeder) { - if (HttpLog.LOGV) HttpLog.v("ConnectionThread: Waiting for work"); - mWaiting = true; - try { - mRequestFeeder.wait(); - } catch (InterruptedException e) { - } - mWaiting = false; - if (mCurrentThreadTime != 0) { - mCurrentThreadTime = SystemClock - .currentThreadTimeMillis(); - } - } - } else { - if (HttpLog.LOGV) HttpLog.v("ConnectionThread: new request " + - request.mHost + " " + request ); - - mConnection = mConnectionManager.getConnection(mContext, - request.mHost); - mConnection.processRequests(request); - if (mConnection.getCanPersist()) { - if (!mConnectionManager.recycleConnection(mConnection)) { - mConnection.closeConnection(); - } - } else { - mConnection.closeConnection(); - } - mConnection = null; - - if (mCurrentThreadTime > 0) { - long start = mCurrentThreadTime; - mCurrentThreadTime = SystemClock.currentThreadTimeMillis(); - mTotalThreadTime += mCurrentThreadTime - start; - } - } - - } - } - - public synchronized String toString() { - String con = mConnection == null ? "" : mConnection.toString(); - String active = mWaiting ? "w" : "a"; - return "cid " + mId + " " + active + " " + con; - } - -} diff --git a/core/java/android/net/http/DelegatingSSLSession.java b/core/java/android/net/http/DelegatingSSLSession.java deleted file mode 100644 index 98fbe21..0000000 --- a/core/java/android/net/http/DelegatingSSLSession.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2014 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.net.http; - -import java.security.Principal; -import java.security.cert.Certificate; -import java.security.cert.X509Certificate; - -import javax.net.ssl.SSLPeerUnverifiedException; -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSessionContext; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.X509TrustManager; - -/** - * This is only used when a {@code certificate} is available but usage - * requires a {@link SSLSession}. - * - * @hide - */ -public class DelegatingSSLSession implements SSLSession { - protected DelegatingSSLSession() { - } - - public static class CertificateWrap extends DelegatingSSLSession { - private final Certificate mCertificate; - - public CertificateWrap(Certificate certificate) { - mCertificate = certificate; - } - - @Override - public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { - return new Certificate[] { mCertificate }; - } - } - - - @Override - public int getApplicationBufferSize() { - throw new UnsupportedOperationException(); - } - - @Override - public String getCipherSuite() { - throw new UnsupportedOperationException(); - } - - @Override - public long getCreationTime() { - throw new UnsupportedOperationException(); - } - - @Override - public byte[] getId() { - throw new UnsupportedOperationException(); - } - - @Override - public long getLastAccessedTime() { - throw new UnsupportedOperationException(); - } - - @Override - public Certificate[] getLocalCertificates() { - throw new UnsupportedOperationException(); - } - - @Override - public Principal getLocalPrincipal() { - throw new UnsupportedOperationException(); - } - - @Override - public int getPacketBufferSize() { - throw new UnsupportedOperationException(); - } - - @Override - public javax.security.cert.X509Certificate[] getPeerCertificateChain() - throws SSLPeerUnverifiedException { - throw new UnsupportedOperationException(); - } - - @Override - public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { - throw new UnsupportedOperationException(); - } - - @Override - public String getPeerHost() { - throw new UnsupportedOperationException(); - } - - @Override - public int getPeerPort() { - throw new UnsupportedOperationException(); - } - - @Override - public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { - throw new UnsupportedOperationException(); - } - - @Override - public String getProtocol() { - throw new UnsupportedOperationException(); - } - - @Override - public SSLSessionContext getSessionContext() { - throw new UnsupportedOperationException(); - } - - @Override - public Object getValue(String name) { - throw new UnsupportedOperationException(); - } - - @Override - public String[] getValueNames() { - throw new UnsupportedOperationException(); - } - - @Override - public void invalidate() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isValid() { - throw new UnsupportedOperationException(); - } - - @Override - public void putValue(String name, Object value) { - throw new UnsupportedOperationException(); - } - - @Override - public void removeValue(String name) { - throw new UnsupportedOperationException(); - } -} diff --git a/core/java/android/net/http/EventHandler.java b/core/java/android/net/http/EventHandler.java deleted file mode 100644 index 3fd471d..0000000 --- a/core/java/android/net/http/EventHandler.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.http; - - -/** - * Callbacks in this interface are made as an HTTP request is - * processed. The normal order of callbacks is status(), headers(), - * then multiple data() then endData(). handleSslErrorRequest(), if - * there is an SSL certificate error. error() can occur anywhere - * in the transaction. - * - * {@hide} - */ - -public interface EventHandler { - - /** - * Error codes used in the error() callback. Positive error codes - * are reserved for codes sent by http servers. Negative error - * codes are connection/parsing failures, etc. - */ - - /** Success */ - public static final int OK = 0; - /** Generic error */ - public static final int ERROR = -1; - /** Server or proxy hostname lookup failed */ - public static final int ERROR_LOOKUP = -2; - /** Unsupported authentication scheme (ie, not basic or digest) */ - public static final int ERROR_UNSUPPORTED_AUTH_SCHEME = -3; - /** User authentication failed on server */ - public static final int ERROR_AUTH = -4; - /** User authentication failed on proxy */ - public static final int ERROR_PROXYAUTH = -5; - /** Could not connect to server */ - public static final int ERROR_CONNECT = -6; - /** Failed to write to or read from server */ - public static final int ERROR_IO = -7; - /** Connection timed out */ - public static final int ERROR_TIMEOUT = -8; - /** Too many redirects */ - public static final int ERROR_REDIRECT_LOOP = -9; - /** Unsupported URI scheme (ie, not http, https, etc) */ - public static final int ERROR_UNSUPPORTED_SCHEME = -10; - /** Failed to perform SSL handshake */ - public static final int ERROR_FAILED_SSL_HANDSHAKE = -11; - /** Bad URL */ - public static final int ERROR_BAD_URL = -12; - /** Generic file error for file:/// loads */ - public static final int FILE_ERROR = -13; - /** File not found error for file:/// loads */ - public static final int FILE_NOT_FOUND_ERROR = -14; - /** Too many requests queued */ - public static final int TOO_MANY_REQUESTS_ERROR = -15; - - /** - * Called after status line has been sucessfully processed. - * @param major_version HTTP version advertised by server. major - * is the part before the "." - * @param minor_version HTTP version advertised by server. minor - * is the part after the "." - * @param code HTTP Status code. See RFC 2616. - * @param reason_phrase Textual explanation sent by server - */ - public void status(int major_version, - int minor_version, - int code, - String reason_phrase); - - /** - * Called after all headers are successfully processed. - */ - public void headers(Headers headers); - - /** - * An array containing all or part of the http body as read from - * the server. - * @param data A byte array containing the content - * @param len The length of valid content in data - * - * Note: chunked and compressed encodings are handled within - * android.net.http. Decoded data is passed through this - * interface. - */ - public void data(byte[] data, int len); - - /** - * Called when the document is completely read. No more data() - * callbacks will be made after this call - */ - public void endData(); - - /** - * SSL certificate callback called before resource request is - * made, which will be null for insecure connection. - */ - public void certificate(SslCertificate certificate); - - /** - * There was trouble. - * @param id One of the error codes defined below - * @param description of error - */ - public void error(int id, String description); - - /** - * SSL certificate error callback. Handles SSL error(s) on the way - * up to the user. The callback has to make sure that restartConnection() is called, - * otherwise the connection will be suspended indefinitely. - * @return True if the callback can handle the error, which means it will - * call restartConnection() to unblock the thread later, - * otherwise return false. - */ - public boolean handleSslErrorRequest(SslError error); - -} diff --git a/core/java/android/net/http/Headers.java b/core/java/android/net/http/Headers.java deleted file mode 100644 index 0f8b105..0000000 --- a/core/java/android/net/http/Headers.java +++ /dev/null @@ -1,521 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.http; - -import android.util.Log; - -import java.util.ArrayList; - -import org.apache.http.HeaderElement; -import org.apache.http.entity.ContentLengthStrategy; -import org.apache.http.message.BasicHeaderValueParser; -import org.apache.http.message.ParserCursor; -import org.apache.http.protocol.HTTP; -import org.apache.http.util.CharArrayBuffer; - -/** - * Manages received headers - * - * {@hide} - */ -public final class Headers { - private static final String LOGTAG = "Http"; - - // header parsing constant - /** - * indicate HTTP 1.0 connection close after the response - */ - public final static int CONN_CLOSE = 1; - /** - * indicate HTTP 1.1 connection keep alive - */ - public final static int CONN_KEEP_ALIVE = 2; - - // initial values. - public final static int NO_CONN_TYPE = 0; - public final static long NO_TRANSFER_ENCODING = 0; - public final static long NO_CONTENT_LENGTH = -1; - - // header strings - public final static String TRANSFER_ENCODING = "transfer-encoding"; - public final static String CONTENT_LEN = "content-length"; - public final static String CONTENT_TYPE = "content-type"; - public final static String CONTENT_ENCODING = "content-encoding"; - public final static String CONN_DIRECTIVE = "connection"; - - public final static String LOCATION = "location"; - public final static String PROXY_CONNECTION = "proxy-connection"; - - public final static String WWW_AUTHENTICATE = "www-authenticate"; - public final static String PROXY_AUTHENTICATE = "proxy-authenticate"; - public final static String CONTENT_DISPOSITION = "content-disposition"; - public final static String ACCEPT_RANGES = "accept-ranges"; - public final static String EXPIRES = "expires"; - public final static String CACHE_CONTROL = "cache-control"; - public final static String LAST_MODIFIED = "last-modified"; - public final static String ETAG = "etag"; - public final static String SET_COOKIE = "set-cookie"; - public final static String PRAGMA = "pragma"; - public final static String REFRESH = "refresh"; - public final static String X_PERMITTED_CROSS_DOMAIN_POLICIES = "x-permitted-cross-domain-policies"; - - // following hash are generated by String.hashCode() - private final static int HASH_TRANSFER_ENCODING = 1274458357; - private final static int HASH_CONTENT_LEN = -1132779846; - private final static int HASH_CONTENT_TYPE = 785670158; - private final static int HASH_CONTENT_ENCODING = 2095084583; - private final static int HASH_CONN_DIRECTIVE = -775651618; - private final static int HASH_LOCATION = 1901043637; - private final static int HASH_PROXY_CONNECTION = 285929373; - private final static int HASH_WWW_AUTHENTICATE = -243037365; - private final static int HASH_PROXY_AUTHENTICATE = -301767724; - private final static int HASH_CONTENT_DISPOSITION = -1267267485; - private final static int HASH_ACCEPT_RANGES = 1397189435; - private final static int HASH_EXPIRES = -1309235404; - private final static int HASH_CACHE_CONTROL = -208775662; - private final static int HASH_LAST_MODIFIED = 150043680; - private final static int HASH_ETAG = 3123477; - private final static int HASH_SET_COOKIE = 1237214767; - private final static int HASH_PRAGMA = -980228804; - private final static int HASH_REFRESH = 1085444827; - private final static int HASH_X_PERMITTED_CROSS_DOMAIN_POLICIES = -1345594014; - - // keep any headers that require direct access in a presized - // string array - private final static int IDX_TRANSFER_ENCODING = 0; - private final static int IDX_CONTENT_LEN = 1; - private final static int IDX_CONTENT_TYPE = 2; - private final static int IDX_CONTENT_ENCODING = 3; - private final static int IDX_CONN_DIRECTIVE = 4; - private final static int IDX_LOCATION = 5; - private final static int IDX_PROXY_CONNECTION = 6; - private final static int IDX_WWW_AUTHENTICATE = 7; - private final static int IDX_PROXY_AUTHENTICATE = 8; - private final static int IDX_CONTENT_DISPOSITION = 9; - private final static int IDX_ACCEPT_RANGES = 10; - private final static int IDX_EXPIRES = 11; - private final static int IDX_CACHE_CONTROL = 12; - private final static int IDX_LAST_MODIFIED = 13; - private final static int IDX_ETAG = 14; - private final static int IDX_SET_COOKIE = 15; - private final static int IDX_PRAGMA = 16; - private final static int IDX_REFRESH = 17; - private final static int IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES = 18; - - private final static int HEADER_COUNT = 19; - - /* parsed values */ - private long transferEncoding; - private long contentLength; // Content length of the incoming data - private int connectionType; - private ArrayList<String> cookies = new ArrayList<String>(2); - - private String[] mHeaders = new String[HEADER_COUNT]; - private final static String[] sHeaderNames = { - TRANSFER_ENCODING, - CONTENT_LEN, - CONTENT_TYPE, - CONTENT_ENCODING, - CONN_DIRECTIVE, - LOCATION, - PROXY_CONNECTION, - WWW_AUTHENTICATE, - PROXY_AUTHENTICATE, - CONTENT_DISPOSITION, - ACCEPT_RANGES, - EXPIRES, - CACHE_CONTROL, - LAST_MODIFIED, - ETAG, - SET_COOKIE, - PRAGMA, - REFRESH, - X_PERMITTED_CROSS_DOMAIN_POLICIES - }; - - // Catch-all for headers not explicitly handled - private ArrayList<String> mExtraHeaderNames = new ArrayList<String>(4); - private ArrayList<String> mExtraHeaderValues = new ArrayList<String>(4); - - public Headers() { - transferEncoding = NO_TRANSFER_ENCODING; - contentLength = NO_CONTENT_LENGTH; - connectionType = NO_CONN_TYPE; - } - - public void parseHeader(CharArrayBuffer buffer) { - int pos = setLowercaseIndexOf(buffer, ':'); - if (pos == -1) { - return; - } - String name = buffer.substringTrimmed(0, pos); - if (name.length() == 0) { - return; - } - pos++; - - String val = buffer.substringTrimmed(pos, buffer.length()); - if (HttpLog.LOGV) { - HttpLog.v("hdr " + buffer.length() + " " + buffer); - } - - switch (name.hashCode()) { - case HASH_TRANSFER_ENCODING: - if (name.equals(TRANSFER_ENCODING)) { - mHeaders[IDX_TRANSFER_ENCODING] = val; - HeaderElement[] encodings = BasicHeaderValueParser.DEFAULT - .parseElements(buffer, new ParserCursor(pos, - buffer.length())); - // The chunked encoding must be the last one applied RFC2616, - // 14.41 - int len = encodings.length; - if (HTTP.IDENTITY_CODING.equalsIgnoreCase(val)) { - transferEncoding = ContentLengthStrategy.IDENTITY; - } else if ((len > 0) - && (HTTP.CHUNK_CODING - .equalsIgnoreCase(encodings[len - 1].getName()))) { - transferEncoding = ContentLengthStrategy.CHUNKED; - } else { - transferEncoding = ContentLengthStrategy.IDENTITY; - } - } - break; - case HASH_CONTENT_LEN: - if (name.equals(CONTENT_LEN)) { - mHeaders[IDX_CONTENT_LEN] = val; - try { - contentLength = Long.parseLong(val); - } catch (NumberFormatException e) { - if (false) { - Log.v(LOGTAG, "Headers.headers(): error parsing" - + " content length: " + buffer.toString()); - } - } - } - break; - case HASH_CONTENT_TYPE: - if (name.equals(CONTENT_TYPE)) { - mHeaders[IDX_CONTENT_TYPE] = val; - } - break; - case HASH_CONTENT_ENCODING: - if (name.equals(CONTENT_ENCODING)) { - mHeaders[IDX_CONTENT_ENCODING] = val; - } - break; - case HASH_CONN_DIRECTIVE: - if (name.equals(CONN_DIRECTIVE)) { - mHeaders[IDX_CONN_DIRECTIVE] = val; - setConnectionType(buffer, pos); - } - break; - case HASH_LOCATION: - if (name.equals(LOCATION)) { - mHeaders[IDX_LOCATION] = val; - } - break; - case HASH_PROXY_CONNECTION: - if (name.equals(PROXY_CONNECTION)) { - mHeaders[IDX_PROXY_CONNECTION] = val; - setConnectionType(buffer, pos); - } - break; - case HASH_WWW_AUTHENTICATE: - if (name.equals(WWW_AUTHENTICATE)) { - mHeaders[IDX_WWW_AUTHENTICATE] = val; - } - break; - case HASH_PROXY_AUTHENTICATE: - if (name.equals(PROXY_AUTHENTICATE)) { - mHeaders[IDX_PROXY_AUTHENTICATE] = val; - } - break; - case HASH_CONTENT_DISPOSITION: - if (name.equals(CONTENT_DISPOSITION)) { - mHeaders[IDX_CONTENT_DISPOSITION] = val; - } - break; - case HASH_ACCEPT_RANGES: - if (name.equals(ACCEPT_RANGES)) { - mHeaders[IDX_ACCEPT_RANGES] = val; - } - break; - case HASH_EXPIRES: - if (name.equals(EXPIRES)) { - mHeaders[IDX_EXPIRES] = val; - } - break; - case HASH_CACHE_CONTROL: - if (name.equals(CACHE_CONTROL)) { - // In case where we receive more than one header, create a ',' separated list. - // This should be ok, according to RFC 2616 chapter 4.2 - if (mHeaders[IDX_CACHE_CONTROL] != null && - mHeaders[IDX_CACHE_CONTROL].length() > 0) { - mHeaders[IDX_CACHE_CONTROL] += (',' + val); - } else { - mHeaders[IDX_CACHE_CONTROL] = val; - } - } - break; - case HASH_LAST_MODIFIED: - if (name.equals(LAST_MODIFIED)) { - mHeaders[IDX_LAST_MODIFIED] = val; - } - break; - case HASH_ETAG: - if (name.equals(ETAG)) { - mHeaders[IDX_ETAG] = val; - } - break; - case HASH_SET_COOKIE: - if (name.equals(SET_COOKIE)) { - mHeaders[IDX_SET_COOKIE] = val; - cookies.add(val); - } - break; - case HASH_PRAGMA: - if (name.equals(PRAGMA)) { - mHeaders[IDX_PRAGMA] = val; - } - break; - case HASH_REFRESH: - if (name.equals(REFRESH)) { - mHeaders[IDX_REFRESH] = val; - } - break; - case HASH_X_PERMITTED_CROSS_DOMAIN_POLICIES: - if (name.equals(X_PERMITTED_CROSS_DOMAIN_POLICIES)) { - mHeaders[IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES] = val; - } - break; - default: - mExtraHeaderNames.add(name); - mExtraHeaderValues.add(val); - } - } - - public long getTransferEncoding() { - return transferEncoding; - } - - public long getContentLength() { - return contentLength; - } - - public int getConnectionType() { - return connectionType; - } - - public String getContentType() { - return mHeaders[IDX_CONTENT_TYPE]; - } - - public String getContentEncoding() { - return mHeaders[IDX_CONTENT_ENCODING]; - } - - public String getLocation() { - return mHeaders[IDX_LOCATION]; - } - - public String getWwwAuthenticate() { - return mHeaders[IDX_WWW_AUTHENTICATE]; - } - - public String getProxyAuthenticate() { - return mHeaders[IDX_PROXY_AUTHENTICATE]; - } - - public String getContentDisposition() { - return mHeaders[IDX_CONTENT_DISPOSITION]; - } - - public String getAcceptRanges() { - return mHeaders[IDX_ACCEPT_RANGES]; - } - - public String getExpires() { - return mHeaders[IDX_EXPIRES]; - } - - public String getCacheControl() { - return mHeaders[IDX_CACHE_CONTROL]; - } - - public String getLastModified() { - return mHeaders[IDX_LAST_MODIFIED]; - } - - public String getEtag() { - return mHeaders[IDX_ETAG]; - } - - public ArrayList<String> getSetCookie() { - return this.cookies; - } - - public String getPragma() { - return mHeaders[IDX_PRAGMA]; - } - - public String getRefresh() { - return mHeaders[IDX_REFRESH]; - } - - public String getXPermittedCrossDomainPolicies() { - return mHeaders[IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES]; - } - - public void setContentLength(long value) { - this.contentLength = value; - } - - public void setContentType(String value) { - mHeaders[IDX_CONTENT_TYPE] = value; - } - - public void setContentEncoding(String value) { - mHeaders[IDX_CONTENT_ENCODING] = value; - } - - public void setLocation(String value) { - mHeaders[IDX_LOCATION] = value; - } - - public void setWwwAuthenticate(String value) { - mHeaders[IDX_WWW_AUTHENTICATE] = value; - } - - public void setProxyAuthenticate(String value) { - mHeaders[IDX_PROXY_AUTHENTICATE] = value; - } - - public void setContentDisposition(String value) { - mHeaders[IDX_CONTENT_DISPOSITION] = value; - } - - public void setAcceptRanges(String value) { - mHeaders[IDX_ACCEPT_RANGES] = value; - } - - public void setExpires(String value) { - mHeaders[IDX_EXPIRES] = value; - } - - public void setCacheControl(String value) { - mHeaders[IDX_CACHE_CONTROL] = value; - } - - public void setLastModified(String value) { - mHeaders[IDX_LAST_MODIFIED] = value; - } - - public void setEtag(String value) { - mHeaders[IDX_ETAG] = value; - } - - public void setXPermittedCrossDomainPolicies(String value) { - mHeaders[IDX_X_PERMITTED_CROSS_DOMAIN_POLICIES] = value; - } - - public interface HeaderCallback { - public void header(String name, String value); - } - - /** - * Reports all non-null headers to the callback - */ - public void getHeaders(HeaderCallback hcb) { - for (int i = 0; i < HEADER_COUNT; i++) { - String h = mHeaders[i]; - if (h != null) { - hcb.header(sHeaderNames[i], h); - } - } - int extraLen = mExtraHeaderNames.size(); - for (int i = 0; i < extraLen; i++) { - if (false) { - HttpLog.v("Headers.getHeaders() extra: " + i + " " + - mExtraHeaderNames.get(i) + " " + mExtraHeaderValues.get(i)); - } - hcb.header(mExtraHeaderNames.get(i), - mExtraHeaderValues.get(i)); - } - - } - - private void setConnectionType(CharArrayBuffer buffer, int pos) { - if (containsIgnoreCaseTrimmed(buffer, pos, HTTP.CONN_CLOSE)) { - connectionType = CONN_CLOSE; - } else if (containsIgnoreCaseTrimmed( - buffer, pos, HTTP.CONN_KEEP_ALIVE)) { - connectionType = CONN_KEEP_ALIVE; - } - } - - - /** - * Returns true if the buffer contains the given string. Ignores leading - * whitespace and case. - * - * @param buffer to search - * @param beginIndex index at which we should start - * @param str to search for - */ - static boolean containsIgnoreCaseTrimmed(CharArrayBuffer buffer, - int beginIndex, final String str) { - int len = buffer.length(); - char[] chars = buffer.buffer(); - while (beginIndex < len && HTTP.isWhitespace(chars[beginIndex])) { - beginIndex++; - } - int size = str.length(); - boolean ok = len >= (beginIndex + size); - for (int j=0; ok && (j < size); j++) { - char a = chars[beginIndex + j]; - char b = str.charAt(j); - if (a != b) { - a = Character.toLowerCase(a); - b = Character.toLowerCase(b); - ok = a == b; - } - } - - return true; - } - - /** - * Returns index of first occurence ch. Lower cases characters leading up - * to first occurrence of ch. - */ - static int setLowercaseIndexOf(CharArrayBuffer buffer, final int ch) { - - int beginIndex = 0; - int endIndex = buffer.length(); - char[] chars = buffer.buffer(); - - for (int i = beginIndex; i < endIndex; i++) { - char current = chars[i]; - if (current == ch) { - return i; - } else { - chars[i] = Character.toLowerCase(current); - } - } - return -1; - } -} diff --git a/core/java/android/net/http/HttpAuthHeader.java b/core/java/android/net/http/HttpAuthHeader.java deleted file mode 100644 index 3abac23..0000000 --- a/core/java/android/net/http/HttpAuthHeader.java +++ /dev/null @@ -1,424 +0,0 @@ -/* - * Copyright (C) 2007 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.net.http; - -import java.util.Locale; - -/** - * HttpAuthHeader: a class to store HTTP authentication-header parameters. - * For more information, see: RFC 2617: HTTP Authentication. - * - * {@hide} - */ -public class HttpAuthHeader { - /** - * Possible HTTP-authentication header tokens to search for: - */ - public final static String BASIC_TOKEN = "Basic"; - public final static String DIGEST_TOKEN = "Digest"; - - private final static String REALM_TOKEN = "realm"; - private final static String NONCE_TOKEN = "nonce"; - private final static String STALE_TOKEN = "stale"; - private final static String OPAQUE_TOKEN = "opaque"; - private final static String QOP_TOKEN = "qop"; - private final static String ALGORITHM_TOKEN = "algorithm"; - - /** - * An authentication scheme. We currently support two different schemes: - * HttpAuthHeader.BASIC - basic, and - * HttpAuthHeader.DIGEST - digest (algorithm=MD5, QOP="auth"). - */ - private int mScheme; - - public static final int UNKNOWN = 0; - public static final int BASIC = 1; - public static final int DIGEST = 2; - - /** - * A flag, indicating that the previous request from the client was - * rejected because the nonce value was stale. If stale is TRUE - * (case-insensitive), the client may wish to simply retry the request - * with a new encrypted response, without reprompting the user for a - * new username and password. - */ - private boolean mStale; - - /** - * A string to be displayed to users so they know which username and - * password to use. - */ - private String mRealm; - - /** - * A server-specified data string which should be uniquely generated - * each time a 401 response is made. - */ - private String mNonce; - - /** - * A string of data, specified by the server, which should be returned - * by the client unchanged in the Authorization header of subsequent - * requests with URIs in the same protection space. - */ - private String mOpaque; - - /** - * This directive is optional, but is made so only for backward - * compatibility with RFC 2069 [6]; it SHOULD be used by all - * implementations compliant with this version of the Digest scheme. - * If present, it is a quoted string of one or more tokens indicating - * the "quality of protection" values supported by the server. The - * value "auth" indicates authentication; the value "auth-int" - * indicates authentication with integrity protection. - */ - private String mQop; - - /** - * A string indicating a pair of algorithms used to produce the digest - * and a checksum. If this is not present it is assumed to be "MD5". - */ - private String mAlgorithm; - - /** - * Is this authentication request a proxy authentication request? - */ - private boolean mIsProxy; - - /** - * Username string we get from the user. - */ - private String mUsername; - - /** - * Password string we get from the user. - */ - private String mPassword; - - /** - * Creates a new HTTP-authentication header object from the - * input header string. - * The header string is assumed to contain parameters of at - * most one authentication-scheme (ensured by the caller). - */ - public HttpAuthHeader(String header) { - if (header != null) { - parseHeader(header); - } - } - - /** - * @return True iff this is a proxy authentication header. - */ - public boolean isProxy() { - return mIsProxy; - } - - /** - * Marks this header as a proxy authentication header. - */ - public void setProxy() { - mIsProxy = true; - } - - /** - * @return The username string. - */ - public String getUsername() { - return mUsername; - } - - /** - * Sets the username string. - */ - public void setUsername(String username) { - mUsername = username; - } - - /** - * @return The password string. - */ - public String getPassword() { - return mPassword; - } - - /** - * Sets the password string. - */ - public void setPassword(String password) { - mPassword = password; - } - - /** - * @return True iff this is the BASIC-authentication request. - */ - public boolean isBasic () { - return mScheme == BASIC; - } - - /** - * @return True iff this is the DIGEST-authentication request. - */ - public boolean isDigest() { - return mScheme == DIGEST; - } - - /** - * @return The authentication scheme requested. We currently - * support two schemes: - * HttpAuthHeader.BASIC - basic, and - * HttpAuthHeader.DIGEST - digest (algorithm=MD5, QOP="auth"). - */ - public int getScheme() { - return mScheme; - } - - /** - * @return True if indicating that the previous request from - * the client was rejected because the nonce value was stale. - */ - public boolean getStale() { - return mStale; - } - - /** - * @return The realm value or null if there is none. - */ - public String getRealm() { - return mRealm; - } - - /** - * @return The nonce value or null if there is none. - */ - public String getNonce() { - return mNonce; - } - - /** - * @return The opaque value or null if there is none. - */ - public String getOpaque() { - return mOpaque; - } - - /** - * @return The QOP ("quality-of_protection") value or null if - * there is none. The QOP value is always lower-case. - */ - public String getQop() { - return mQop; - } - - /** - * @return The name of the algorithm used or null if there is - * none. By default, MD5 is used. - */ - public String getAlgorithm() { - return mAlgorithm; - } - - /** - * @return True iff the authentication scheme requested by the - * server is supported; currently supported schemes: - * BASIC, - * DIGEST (only algorithm="md5", no qop or qop="auth). - */ - public boolean isSupportedScheme() { - // it is a good idea to enforce non-null realms! - if (mRealm != null) { - if (mScheme == BASIC) { - return true; - } else { - if (mScheme == DIGEST) { - return - mAlgorithm.equals("md5") && - (mQop == null || mQop.equals("auth")); - } - } - } - - return false; - } - - /** - * Parses the header scheme name and then scheme parameters if - * the scheme is supported. - */ - private void parseHeader(String header) { - if (HttpLog.LOGV) { - HttpLog.v("HttpAuthHeader.parseHeader(): header: " + header); - } - - if (header != null) { - String parameters = parseScheme(header); - if (parameters != null) { - // if we have a supported scheme - if (mScheme != UNKNOWN) { - parseParameters(parameters); - } - } - } - } - - /** - * Parses the authentication scheme name. If we have a Digest - * scheme, sets the algorithm value to the default of MD5. - * @return The authentication scheme parameters string to be - * parsed later (if the scheme is supported) or null if failed - * to parse the scheme (the header value is null?). - */ - private String parseScheme(String header) { - if (header != null) { - int i = header.indexOf(' '); - if (i >= 0) { - String scheme = header.substring(0, i).trim(); - if (scheme.equalsIgnoreCase(DIGEST_TOKEN)) { - mScheme = DIGEST; - - // md5 is the default algorithm!!! - mAlgorithm = "md5"; - } else { - if (scheme.equalsIgnoreCase(BASIC_TOKEN)) { - mScheme = BASIC; - } - } - - return header.substring(i + 1); - } - } - - return null; - } - - /** - * Parses a comma-separated list of authentification scheme - * parameters. - */ - private void parseParameters(String parameters) { - if (HttpLog.LOGV) { - HttpLog.v("HttpAuthHeader.parseParameters():" + - " parameters: " + parameters); - } - - if (parameters != null) { - int i; - do { - i = parameters.indexOf(','); - if (i < 0) { - // have only one parameter - parseParameter(parameters); - } else { - parseParameter(parameters.substring(0, i)); - parameters = parameters.substring(i + 1); - } - } while (i >= 0); - } - } - - /** - * Parses a single authentication scheme parameter. The parameter - * string is expected to follow the format: PARAMETER=VALUE. - */ - private void parseParameter(String parameter) { - if (parameter != null) { - // here, we are looking for the 1st occurence of '=' only!!! - int i = parameter.indexOf('='); - if (i >= 0) { - String token = parameter.substring(0, i).trim(); - String value = - trimDoubleQuotesIfAny(parameter.substring(i + 1).trim()); - - if (HttpLog.LOGV) { - HttpLog.v("HttpAuthHeader.parseParameter():" + - " token: " + token + - " value: " + value); - } - - if (token.equalsIgnoreCase(REALM_TOKEN)) { - mRealm = value; - } else { - if (mScheme == DIGEST) { - parseParameter(token, value); - } - } - } - } - } - - /** - * If the token is a known parameter name, parses and initializes - * the token value. - */ - private void parseParameter(String token, String value) { - if (token != null && value != null) { - if (token.equalsIgnoreCase(NONCE_TOKEN)) { - mNonce = value; - return; - } - - if (token.equalsIgnoreCase(STALE_TOKEN)) { - parseStale(value); - return; - } - - if (token.equalsIgnoreCase(OPAQUE_TOKEN)) { - mOpaque = value; - return; - } - - if (token.equalsIgnoreCase(QOP_TOKEN)) { - mQop = value.toLowerCase(Locale.ROOT); - return; - } - - if (token.equalsIgnoreCase(ALGORITHM_TOKEN)) { - mAlgorithm = value.toLowerCase(Locale.ROOT); - return; - } - } - } - - /** - * Parses and initializes the 'stale' paramer value. Any value - * different from case-insensitive "true" is considered "false". - */ - private void parseStale(String value) { - if (value != null) { - if (value.equalsIgnoreCase("true")) { - mStale = true; - } - } - } - - /** - * Trims double-quotes around a parameter value if there are any. - * @return The string value without the outermost pair of double- - * quotes or null if the original value is null. - */ - static private String trimDoubleQuotesIfAny(String value) { - if (value != null) { - int len = value.length(); - if (len > 2 && - value.charAt(0) == '\"' && value.charAt(len - 1) == '\"') { - return value.substring(1, len - 1); - } - } - - return value; - } -} diff --git a/core/java/android/net/http/HttpConnection.java b/core/java/android/net/http/HttpConnection.java deleted file mode 100644 index edf8fed3..0000000 --- a/core/java/android/net/http/HttpConnection.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2007 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.net.http; - -import android.content.Context; - -import java.net.Socket; -import java.io.IOException; - -import org.apache.http.HttpHost; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.HttpConnectionParams; - -/** - * A requestConnection connecting to a normal (non secure) http server - * - * {@hide} - */ -class HttpConnection extends Connection { - - HttpConnection(Context context, HttpHost host, - RequestFeeder requestFeeder) { - super(context, host, requestFeeder); - } - - /** - * Opens the connection to a http server - * - * @return the opened low level connection - * @throws IOException if the connection fails for any reason. - */ - @Override - AndroidHttpClientConnection openConnection(Request req) throws IOException { - - // Update the certificate info (connection not secure - set to null) - EventHandler eventHandler = req.getEventHandler(); - mCertificate = null; - eventHandler.certificate(mCertificate); - - AndroidHttpClientConnection conn = new AndroidHttpClientConnection(); - BasicHttpParams params = new BasicHttpParams(); - Socket sock = new Socket(mHost.getHostName(), mHost.getPort()); - params.setIntParameter(HttpConnectionParams.SOCKET_BUFFER_SIZE, 8192); - conn.bind(sock, params); - return conn; - } - - /** - * Closes the low level connection. - * - * If an exception is thrown then it is assumed that the - * connection will have been closed (to the extent possible) - * anyway and the caller does not need to take any further action. - * - */ - void closeConnection() { - try { - if (mHttpClientConnection != null && mHttpClientConnection.isOpen()) { - mHttpClientConnection.close(); - } - } catch (IOException e) { - if (HttpLog.LOGV) HttpLog.v( - "closeConnection(): failed closing connection " + - mHost); - e.printStackTrace(); - } - } - - /** - * Restart a secure connection suspended waiting for user interaction. - */ - void restartConnection(boolean abort) { - // not required for plain http connections - } - - String getScheme() { - return "http"; - } -} diff --git a/core/java/android/net/http/HttpLog.java b/core/java/android/net/http/HttpLog.java deleted file mode 100644 index 0934664..0000000 --- a/core/java/android/net/http/HttpLog.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2007 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-level logging flag - */ - -package android.net.http; - -import android.os.SystemClock; - -import android.util.Log; - -/** - * {@hide} - */ -class HttpLog { - private final static String LOGTAG = "http"; - - private static final boolean DEBUG = false; - static final boolean LOGV = false; - - static void v(String logMe) { - Log.v(LOGTAG, SystemClock.uptimeMillis() + " " + Thread.currentThread().getName() + " " + logMe); - } - - static void e(String logMe) { - Log.e(LOGTAG, logMe); - } -} diff --git a/core/java/android/net/http/HttpResponseCache.java b/core/java/android/net/http/HttpResponseCache.java index c6c22e7..188287f 100644 --- a/core/java/android/net/http/HttpResponseCache.java +++ b/core/java/android/net/http/HttpResponseCache.java @@ -35,8 +35,8 @@ import java.util.Map; * Caches HTTP and HTTPS responses to the filesystem so they may be reused, * saving time and bandwidth. This class supports {@link * java.net.HttpURLConnection} and {@link javax.net.ssl.HttpsURLConnection}; - * there is no platform-provided cache for {@link - * org.apache.http.impl.client.DefaultHttpClient} or {@link AndroidHttpClient}. + * there is no platform-provided cache for {@code DefaultHttpClient} or + * {@code AndroidHttpClient}. * * <h3>Installing an HTTP response cache</h3> * Enable caching of all of your application's HTTP requests by installing the diff --git a/core/java/android/net/http/HttpsConnection.java b/core/java/android/net/http/HttpsConnection.java deleted file mode 100644 index a8674de..0000000 --- a/core/java/android/net/http/HttpsConnection.java +++ /dev/null @@ -1,433 +0,0 @@ -/* - * Copyright (C) 2007 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.net.http; - -import android.content.Context; -import android.util.Log; -import com.android.org.conscrypt.FileClientSessionCache; -import com.android.org.conscrypt.OpenSSLContextImpl; -import com.android.org.conscrypt.SSLClientSessionCache; -import org.apache.http.Header; -import org.apache.http.HttpException; -import org.apache.http.HttpHost; -import org.apache.http.HttpStatus; -import org.apache.http.ParseException; -import org.apache.http.ProtocolVersion; -import org.apache.http.StatusLine; -import org.apache.http.message.BasicHttpRequest; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.HttpConnectionParams; -import org.apache.http.params.HttpParams; - -import javax.net.ssl.SSLException; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; -import java.io.File; -import java.io.IOException; -import java.net.Socket; -import java.security.KeyManagementException; -import java.security.cert.X509Certificate; -import java.util.Locale; - -/** - * A Connection connecting to a secure http server or tunneling through - * a http proxy server to a https server. - * - * @hide - */ -public class HttpsConnection extends Connection { - - /** - * SSL socket factory - */ - private static SSLSocketFactory mSslSocketFactory = null; - - static { - // This initialization happens in the zygote. It triggers some - // lazy initialization that can will benefit later invocations of - // initializeEngine(). - initializeEngine(null); - } - - /** - * @hide - * - * @param sessionDir directory to cache SSL sessions - */ - public static void initializeEngine(File sessionDir) { - try { - SSLClientSessionCache cache = null; - if (sessionDir != null) { - Log.d("HttpsConnection", "Caching SSL sessions in " - + sessionDir + "."); - cache = FileClientSessionCache.usingDirectory(sessionDir); - } - - OpenSSLContextImpl sslContext = OpenSSLContextImpl.getPreferred(); - - // here, trust managers is a single trust-all manager - TrustManager[] trustManagers = new TrustManager[] { - new X509TrustManager() { - public X509Certificate[] getAcceptedIssuers() { - return null; - } - - public void checkClientTrusted( - X509Certificate[] certs, String authType) { - } - - public void checkServerTrusted( - X509Certificate[] certs, String authType) { - } - } - }; - - sslContext.engineInit(null, trustManagers, null); - sslContext.engineGetClientSessionContext().setPersistentCache(cache); - - synchronized (HttpsConnection.class) { - mSslSocketFactory = sslContext.engineGetSocketFactory(); - } - } catch (KeyManagementException e) { - throw new RuntimeException(e); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private synchronized static SSLSocketFactory getSocketFactory() { - return mSslSocketFactory; - } - - /** - * Object to wait on when suspending the SSL connection - */ - private Object mSuspendLock = new Object(); - - /** - * True if the connection is suspended pending the result of asking the - * user about an error. - */ - private boolean mSuspended = false; - - /** - * True if the connection attempt should be aborted due to an ssl - * error. - */ - private boolean mAborted = false; - - // Used when connecting through a proxy. - private HttpHost mProxyHost; - - /** - * Contructor for a https connection. - */ - HttpsConnection(Context context, HttpHost host, HttpHost proxy, - RequestFeeder requestFeeder) { - super(context, host, requestFeeder); - mProxyHost = proxy; - } - - /** - * Sets the server SSL certificate associated with this - * connection. - * @param certificate The SSL certificate - */ - /* package */ void setCertificate(SslCertificate certificate) { - mCertificate = certificate; - } - - /** - * Opens the connection to a http server or proxy. - * - * @return the opened low level connection - * @throws IOException if the connection fails for any reason. - */ - @Override - AndroidHttpClientConnection openConnection(Request req) throws IOException { - SSLSocket sslSock = null; - - if (mProxyHost != null) { - // If we have a proxy set, we first send a CONNECT request - // to the proxy; if the proxy returns 200 OK, we negotiate - // a secure connection to the target server via the proxy. - // If the request fails, we drop it, but provide the event - // handler with the response status and headers. The event - // handler is then responsible for cancelling the load or - // issueing a new request. - AndroidHttpClientConnection proxyConnection = null; - Socket proxySock = null; - try { - proxySock = new Socket - (mProxyHost.getHostName(), mProxyHost.getPort()); - - proxySock.setSoTimeout(60 * 1000); - - proxyConnection = new AndroidHttpClientConnection(); - HttpParams params = new BasicHttpParams(); - HttpConnectionParams.setSocketBufferSize(params, 8192); - - proxyConnection.bind(proxySock, params); - } catch(IOException e) { - if (proxyConnection != null) { - proxyConnection.close(); - } - - String errorMessage = e.getMessage(); - if (errorMessage == null) { - errorMessage = - "failed to establish a connection to the proxy"; - } - - throw new IOException(errorMessage); - } - - StatusLine statusLine = null; - int statusCode = 0; - Headers headers = new Headers(); - try { - BasicHttpRequest proxyReq = new BasicHttpRequest - ("CONNECT", mHost.toHostString()); - - // add all 'proxy' headers from the original request, we also need - // to add 'host' header unless we want proxy to answer us with a - // 400 Bad Request - for (Header h : req.mHttpRequest.getAllHeaders()) { - String headerName = h.getName().toLowerCase(Locale.ROOT); - if (headerName.startsWith("proxy") || headerName.equals("keep-alive") - || headerName.equals("host")) { - proxyReq.addHeader(h); - } - } - - proxyConnection.sendRequestHeader(proxyReq); - proxyConnection.flush(); - - // it is possible to receive informational status - // codes prior to receiving actual headers; - // all those status codes are smaller than OK 200 - // a loop is a standard way of dealing with them - do { - statusLine = proxyConnection.parseResponseHeader(headers); - statusCode = statusLine.getStatusCode(); - } while (statusCode < HttpStatus.SC_OK); - } catch (ParseException e) { - String errorMessage = e.getMessage(); - if (errorMessage == null) { - errorMessage = - "failed to send a CONNECT request"; - } - - throw new IOException(errorMessage); - } catch (HttpException e) { - String errorMessage = e.getMessage(); - if (errorMessage == null) { - errorMessage = - "failed to send a CONNECT request"; - } - - throw new IOException(errorMessage); - } catch (IOException e) { - String errorMessage = e.getMessage(); - if (errorMessage == null) { - errorMessage = - "failed to send a CONNECT request"; - } - - throw new IOException(errorMessage); - } - - if (statusCode == HttpStatus.SC_OK) { - try { - sslSock = (SSLSocket) getSocketFactory().createSocket( - proxySock, mHost.getHostName(), mHost.getPort(), true); - } catch(IOException e) { - if (sslSock != null) { - sslSock.close(); - } - - String errorMessage = e.getMessage(); - if (errorMessage == null) { - errorMessage = - "failed to create an SSL socket"; - } - throw new IOException(errorMessage); - } - } else { - // if the code is not OK, inform the event handler - ProtocolVersion version = statusLine.getProtocolVersion(); - - req.mEventHandler.status(version.getMajor(), - version.getMinor(), - statusCode, - statusLine.getReasonPhrase()); - req.mEventHandler.headers(headers); - req.mEventHandler.endData(); - - proxyConnection.close(); - - // here, we return null to indicate that the original - // request needs to be dropped - return null; - } - } else { - // if we do not have a proxy, we simply connect to the host - try { - sslSock = (SSLSocket) getSocketFactory().createSocket( - mHost.getHostName(), mHost.getPort()); - sslSock.setSoTimeout(SOCKET_TIMEOUT); - } catch(IOException e) { - if (sslSock != null) { - sslSock.close(); - } - - String errorMessage = e.getMessage(); - if (errorMessage == null) { - errorMessage = "failed to create an SSL socket"; - } - - throw new IOException(errorMessage); - } - } - - // do handshake and validate server certificates - SslError error = CertificateChainValidator.getInstance(). - doHandshakeAndValidateServerCertificates(this, sslSock, mHost.getHostName()); - - // Inform the user if there is a problem - if (error != null) { - // handleSslErrorRequest may immediately unsuspend if it wants to - // allow the certificate anyway. - // So we mark the connection as suspended, call handleSslErrorRequest - // then check if we're still suspended and only wait if we actually - // need to. - synchronized (mSuspendLock) { - mSuspended = true; - } - // don't hold the lock while calling out to the event handler - boolean canHandle = req.getEventHandler().handleSslErrorRequest(error); - if(!canHandle) { - throw new IOException("failed to handle "+ error); - } - synchronized (mSuspendLock) { - if (mSuspended) { - try { - // Put a limit on how long we are waiting; if the timeout - // expires (which should never happen unless you choose - // to ignore the SSL error dialog for a very long time), - // we wake up the thread and abort the request. This is - // to prevent us from stalling the network if things go - // very bad. - mSuspendLock.wait(10 * 60 * 1000); - if (mSuspended) { - // mSuspended is true if we have not had a chance to - // restart the connection yet (ie, the wait timeout - // has expired) - mSuspended = false; - mAborted = true; - if (HttpLog.LOGV) { - HttpLog.v("HttpsConnection.openConnection():" + - " SSL timeout expired and request was cancelled!!!"); - } - } - } catch (InterruptedException e) { - // ignore - } - } - if (mAborted) { - // The user decided not to use this unverified connection - // so close it immediately. - sslSock.close(); - throw new SSLConnectionClosedByUserException("connection closed by the user"); - } - } - } - - // All went well, we have an open, verified connection. - AndroidHttpClientConnection conn = new AndroidHttpClientConnection(); - BasicHttpParams params = new BasicHttpParams(); - params.setIntParameter(HttpConnectionParams.SOCKET_BUFFER_SIZE, 8192); - conn.bind(sslSock, params); - - return conn; - } - - /** - * Closes the low level connection. - * - * If an exception is thrown then it is assumed that the connection will - * have been closed (to the extent possible) anyway and the caller does not - * need to take any further action. - * - */ - @Override - void closeConnection() { - // if the connection has been suspended due to an SSL error - if (mSuspended) { - // wake up the network thread - restartConnection(false); - } - - try { - if (mHttpClientConnection != null && mHttpClientConnection.isOpen()) { - mHttpClientConnection.close(); - } - } catch (IOException e) { - if (HttpLog.LOGV) - HttpLog.v("HttpsConnection.closeConnection():" + - " failed closing connection " + mHost); - e.printStackTrace(); - } - } - - /** - * Restart a secure connection suspended waiting for user interaction. - */ - void restartConnection(boolean proceed) { - if (HttpLog.LOGV) { - HttpLog.v("HttpsConnection.restartConnection():" + - " proceed: " + proceed); - } - - synchronized (mSuspendLock) { - if (mSuspended) { - mSuspended = false; - mAborted = !proceed; - mSuspendLock.notify(); - } - } - } - - @Override - String getScheme() { - return "https"; - } -} - -/** - * Simple exception we throw if the SSL connection is closed by the user. - * - * {@hide} - */ -class SSLConnectionClosedByUserException extends SSLException { - - public SSLConnectionClosedByUserException(String reason) { - super(reason); - } -} diff --git a/core/java/android/net/http/IdleCache.java b/core/java/android/net/http/IdleCache.java deleted file mode 100644 index fda6009..0000000 --- a/core/java/android/net/http/IdleCache.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Hangs onto idle live connections for a little while - */ - -package android.net.http; - -import org.apache.http.HttpHost; - -import android.os.SystemClock; - -/** - * {@hide} - */ -class IdleCache { - - class Entry { - HttpHost mHost; - Connection mConnection; - long mTimeout; - }; - - private final static int IDLE_CACHE_MAX = 8; - - /* Allow five consecutive empty queue checks before shutdown */ - private final static int EMPTY_CHECK_MAX = 5; - - /* six second timeout for connections */ - private final static int TIMEOUT = 6 * 1000; - private final static int CHECK_INTERVAL = 2 * 1000; - private Entry[] mEntries = new Entry[IDLE_CACHE_MAX]; - - private int mCount = 0; - - private IdleReaper mThread = null; - - /* stats */ - private int mCached = 0; - private int mReused = 0; - - IdleCache() { - for (int i = 0; i < IDLE_CACHE_MAX; i++) { - mEntries[i] = new Entry(); - } - } - - /** - * Caches connection, if there is room. - * @return true if connection cached - */ - synchronized boolean cacheConnection( - HttpHost host, Connection connection) { - - boolean ret = false; - - if (HttpLog.LOGV) { - HttpLog.v("IdleCache size " + mCount + " host " + host); - } - - if (mCount < IDLE_CACHE_MAX) { - long time = SystemClock.uptimeMillis(); - for (int i = 0; i < IDLE_CACHE_MAX; i++) { - Entry entry = mEntries[i]; - if (entry.mHost == null) { - entry.mHost = host; - entry.mConnection = connection; - entry.mTimeout = time + TIMEOUT; - mCount++; - if (HttpLog.LOGV) mCached++; - ret = true; - if (mThread == null) { - mThread = new IdleReaper(); - mThread.start(); - } - break; - } - } - } - return ret; - } - - synchronized Connection getConnection(HttpHost host) { - Connection ret = null; - - if (mCount > 0) { - for (int i = 0; i < IDLE_CACHE_MAX; i++) { - Entry entry = mEntries[i]; - HttpHost eHost = entry.mHost; - if (eHost != null && eHost.equals(host)) { - ret = entry.mConnection; - entry.mHost = null; - entry.mConnection = null; - mCount--; - if (HttpLog.LOGV) mReused++; - break; - } - } - } - return ret; - } - - synchronized void clear() { - for (int i = 0; mCount > 0 && i < IDLE_CACHE_MAX; i++) { - Entry entry = mEntries[i]; - if (entry.mHost != null) { - entry.mHost = null; - entry.mConnection.closeConnection(); - entry.mConnection = null; - mCount--; - } - } - } - - private synchronized void clearIdle() { - if (mCount > 0) { - long time = SystemClock.uptimeMillis(); - for (int i = 0; i < IDLE_CACHE_MAX; i++) { - Entry entry = mEntries[i]; - if (entry.mHost != null && time > entry.mTimeout) { - entry.mHost = null; - entry.mConnection.closeConnection(); - entry.mConnection = null; - mCount--; - } - } - } - } - - private class IdleReaper extends Thread { - - public void run() { - int check = 0; - - setName("IdleReaper"); - android.os.Process.setThreadPriority( - android.os.Process.THREAD_PRIORITY_BACKGROUND); - synchronized (IdleCache.this) { - while (check < EMPTY_CHECK_MAX) { - try { - IdleCache.this.wait(CHECK_INTERVAL); - } catch (InterruptedException ex) { - } - if (mCount == 0) { - check++; - } else { - check = 0; - clearIdle(); - } - } - mThread = null; - } - if (HttpLog.LOGV) { - HttpLog.v("IdleCache IdleReaper shutdown: cached " + mCached + - " reused " + mReused); - mCached = 0; - mReused = 0; - } - } - } -} diff --git a/core/java/android/net/http/LoggingEventHandler.java b/core/java/android/net/http/LoggingEventHandler.java deleted file mode 100644 index bdafa0b..0000000 --- a/core/java/android/net/http/LoggingEventHandler.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * A test EventHandler: Logs everything received - */ - -package android.net.http; - -import android.net.http.Headers; - -/** - * {@hide} - */ -public class LoggingEventHandler implements EventHandler { - - public void requestSent() { - HttpLog.v("LoggingEventHandler:requestSent()"); - } - - public void status(int major_version, - int minor_version, - int code, /* Status-Code value */ - String reason_phrase) { - if (HttpLog.LOGV) { - HttpLog.v("LoggingEventHandler:status() major: " + major_version + - " minor: " + minor_version + - " code: " + code + - " reason: " + reason_phrase); - } - } - - public void headers(Headers headers) { - if (HttpLog.LOGV) { - HttpLog.v("LoggingEventHandler:headers()"); - HttpLog.v(headers.toString()); - } - } - - public void locationChanged(String newLocation, boolean permanent) { - if (HttpLog.LOGV) { - HttpLog.v("LoggingEventHandler: locationChanged() " + newLocation + - " permanent " + permanent); - } - } - - public void data(byte[] data, int len) { - if (HttpLog.LOGV) { - HttpLog.v("LoggingEventHandler: data() " + len + " bytes"); - } - // HttpLog.v(new String(data, 0, len)); - } - public void endData() { - if (HttpLog.LOGV) { - HttpLog.v("LoggingEventHandler: endData() called"); - } - } - - public void certificate(SslCertificate certificate) { - if (HttpLog.LOGV) { - HttpLog.v("LoggingEventHandler: certificate(): " + certificate); - } - } - - public void error(int id, String description) { - if (HttpLog.LOGV) { - HttpLog.v("LoggingEventHandler: error() called Id:" + id + - " description " + description); - } - } - - public boolean handleSslErrorRequest(SslError error) { - if (HttpLog.LOGV) { - HttpLog.v("LoggingEventHandler: handleSslErrorRequest():" + error); - } - // return false so that the caller thread won't wait forever - return false; - } -} diff --git a/core/java/android/net/http/Request.java b/core/java/android/net/http/Request.java deleted file mode 100644 index 76d7bb9..0000000 --- a/core/java/android/net/http/Request.java +++ /dev/null @@ -1,526 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.http; - -import java.io.EOFException; -import java.io.InputStream; -import java.io.IOException; -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; -import java.util.zip.GZIPInputStream; - -import org.apache.http.entity.InputStreamEntity; -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.HttpEntityEnclosingRequest; -import org.apache.http.HttpException; -import org.apache.http.HttpHost; -import org.apache.http.HttpRequest; -import org.apache.http.HttpStatus; -import org.apache.http.ParseException; -import org.apache.http.ProtocolVersion; - -import org.apache.http.StatusLine; -import org.apache.http.message.BasicHttpRequest; -import org.apache.http.message.BasicHttpEntityEnclosingRequest; -import org.apache.http.protocol.RequestContent; - -/** - * Represents an HTTP request for a given host. - * - * {@hide} - */ - -class Request { - - /** The eventhandler to call as the request progresses */ - EventHandler mEventHandler; - - private Connection mConnection; - - /** The Apache http request */ - BasicHttpRequest mHttpRequest; - - /** The path component of this request */ - String mPath; - - /** Host serving this request */ - HttpHost mHost; - - /** Set if I'm using a proxy server */ - HttpHost mProxyHost; - - /** True if request has been cancelled */ - volatile boolean mCancelled = false; - - int mFailCount = 0; - - // This will be used to set the Range field if we retry a connection. This - // is http/1.1 feature. - private int mReceivedBytes = 0; - - private InputStream mBodyProvider; - private int mBodyLength; - - private final static String HOST_HEADER = "Host"; - private final static String ACCEPT_ENCODING_HEADER = "Accept-Encoding"; - private final static String CONTENT_LENGTH_HEADER = "content-length"; - - /* Used to synchronize waitUntilComplete() requests */ - private final Object mClientResource = new Object(); - - /** True if loading should be paused **/ - private boolean mLoadingPaused = false; - - /** - * Processor used to set content-length and transfer-encoding - * headers. - */ - private static RequestContent requestContentProcessor = - new RequestContent(); - - /** - * Instantiates a new Request. - * @param method GET/POST/PUT - * @param host The server that will handle this request - * @param path path part of URI - * @param bodyProvider InputStream providing HTTP body, null if none - * @param bodyLength length of body, must be 0 if bodyProvider is null - * @param eventHandler request will make progress callbacks on - * this interface - * @param headers reqeust headers - */ - Request(String method, HttpHost host, HttpHost proxyHost, String path, - InputStream bodyProvider, int bodyLength, - EventHandler eventHandler, - Map<String, String> headers) { - mEventHandler = eventHandler; - mHost = host; - mProxyHost = proxyHost; - mPath = path; - mBodyProvider = bodyProvider; - mBodyLength = bodyLength; - - if (bodyProvider == null && !"POST".equalsIgnoreCase(method)) { - mHttpRequest = new BasicHttpRequest(method, getUri()); - } else { - mHttpRequest = new BasicHttpEntityEnclosingRequest( - method, getUri()); - // it is ok to have null entity for BasicHttpEntityEnclosingRequest. - // By using BasicHttpEntityEnclosingRequest, it will set up the - // correct content-length, content-type and content-encoding. - if (bodyProvider != null) { - setBodyProvider(bodyProvider, bodyLength); - } - } - addHeader(HOST_HEADER, getHostPort()); - - /* FIXME: if webcore will make the root document a - high-priority request, we can ask for gzip encoding only on - high priority reqs (saving the trouble for images, etc) */ - addHeader(ACCEPT_ENCODING_HEADER, "gzip"); - addHeaders(headers); - } - - /** - * @param pause True if the load should be paused. - */ - synchronized void setLoadingPaused(boolean pause) { - mLoadingPaused = pause; - - // Wake up the paused thread if we're unpausing the load. - if (!mLoadingPaused) { - notify(); - } - } - - /** - * @param connection Request served by this connection - */ - void setConnection(Connection connection) { - mConnection = connection; - } - - /* package */ EventHandler getEventHandler() { - return mEventHandler; - } - - /** - * Add header represented by given pair to request. Header will - * be formatted in request as "name: value\r\n". - * @param name of header - * @param value of header - */ - void addHeader(String name, String value) { - if (name == null) { - String damage = "Null http header name"; - HttpLog.e(damage); - throw new NullPointerException(damage); - } - if (value == null || value.length() == 0) { - String damage = "Null or empty value for header \"" + name + "\""; - HttpLog.e(damage); - throw new RuntimeException(damage); - } - mHttpRequest.addHeader(name, value); - } - - /** - * Add all headers in given map to this request. This is a helper - * method: it calls addHeader for each pair in the map. - */ - void addHeaders(Map<String, String> headers) { - if (headers == null) { - return; - } - - Entry<String, String> entry; - Iterator<Entry<String, String>> i = headers.entrySet().iterator(); - while (i.hasNext()) { - entry = i.next(); - addHeader(entry.getKey(), entry.getValue()); - } - } - - /** - * Send the request line and headers - */ - void sendRequest(AndroidHttpClientConnection httpClientConnection) - throws HttpException, IOException { - - if (mCancelled) return; // don't send cancelled requests - - if (HttpLog.LOGV) { - HttpLog.v("Request.sendRequest() " + mHost.getSchemeName() + "://" + getHostPort()); - // HttpLog.v(mHttpRequest.getRequestLine().toString()); - if (false) { - Iterator i = mHttpRequest.headerIterator(); - while (i.hasNext()) { - Header header = (Header)i.next(); - HttpLog.v(header.getName() + ": " + header.getValue()); - } - } - } - - requestContentProcessor.process(mHttpRequest, - mConnection.getHttpContext()); - httpClientConnection.sendRequestHeader(mHttpRequest); - if (mHttpRequest instanceof HttpEntityEnclosingRequest) { - httpClientConnection.sendRequestEntity( - (HttpEntityEnclosingRequest) mHttpRequest); - } - - if (HttpLog.LOGV) { - HttpLog.v("Request.requestSent() " + mHost.getSchemeName() + "://" + getHostPort() + mPath); - } - } - - - /** - * Receive a single http response. - * - * @param httpClientConnection the request to receive the response for. - */ - void readResponse(AndroidHttpClientConnection httpClientConnection) - throws IOException, ParseException { - - if (mCancelled) return; // don't send cancelled requests - - StatusLine statusLine = null; - boolean hasBody = false; - httpClientConnection.flush(); - int statusCode = 0; - - Headers header = new Headers(); - do { - statusLine = httpClientConnection.parseResponseHeader(header); - statusCode = statusLine.getStatusCode(); - } while (statusCode < HttpStatus.SC_OK); - if (HttpLog.LOGV) HttpLog.v( - "Request.readResponseStatus() " + - statusLine.toString().length() + " " + statusLine); - - ProtocolVersion v = statusLine.getProtocolVersion(); - mEventHandler.status(v.getMajor(), v.getMinor(), - statusCode, statusLine.getReasonPhrase()); - mEventHandler.headers(header); - HttpEntity entity = null; - hasBody = canResponseHaveBody(mHttpRequest, statusCode); - - if (hasBody) - entity = httpClientConnection.receiveResponseEntity(header); - - // restrict the range request to the servers claiming that they are - // accepting ranges in bytes - boolean supportPartialContent = "bytes".equalsIgnoreCase(header - .getAcceptRanges()); - - if (entity != null) { - InputStream is = entity.getContent(); - - // process gzip content encoding - Header contentEncoding = entity.getContentEncoding(); - InputStream nis = null; - byte[] buf = null; - int count = 0; - try { - if (contentEncoding != null && - contentEncoding.getValue().equals("gzip")) { - nis = new GZIPInputStream(is); - } else { - nis = is; - } - - /* accumulate enough data to make it worth pushing it - * up the stack */ - buf = mConnection.getBuf(); - int len = 0; - int lowWater = buf.length / 2; - while (len != -1) { - synchronized(this) { - while (mLoadingPaused) { - // Put this (network loading) thread to sleep if WebCore - // has asked us to. This can happen with plugins for - // example, if we are streaming data but the plugin has - // filled its internal buffers. - try { - wait(); - } catch (InterruptedException e) { - HttpLog.e("Interrupted exception whilst " - + "network thread paused at WebCore's request." - + " " + e.getMessage()); - } - } - } - - len = nis.read(buf, count, buf.length - count); - - if (len != -1) { - count += len; - if (supportPartialContent) mReceivedBytes += len; - } - if (len == -1 || count >= lowWater) { - if (HttpLog.LOGV) HttpLog.v("Request.readResponse() " + count); - mEventHandler.data(buf, count); - count = 0; - } - } - } catch (EOFException e) { - /* InflaterInputStream throws an EOFException when the - server truncates gzipped content. Handle this case - as we do truncated non-gzipped content: no error */ - if (count > 0) { - // if there is uncommited content, we should commit them - mEventHandler.data(buf, count); - } - if (HttpLog.LOGV) HttpLog.v( "readResponse() handling " + e); - } catch(IOException e) { - // don't throw if we have a non-OK status code - if (statusCode == HttpStatus.SC_OK - || statusCode == HttpStatus.SC_PARTIAL_CONTENT) { - if (supportPartialContent && count > 0) { - // if there is uncommited content, we should commit them - // as we will continue the request - mEventHandler.data(buf, count); - } - throw e; - } - } finally { - if (nis != null) { - nis.close(); - } - } - } - mConnection.setCanPersist(entity, statusLine.getProtocolVersion(), - header.getConnectionType()); - mEventHandler.endData(); - complete(); - - if (HttpLog.LOGV) HttpLog.v("Request.readResponse(): done " + - mHost.getSchemeName() + "://" + getHostPort() + mPath); - } - - /** - * Data will not be sent to or received from server after cancel() - * call. Does not close connection--use close() below for that. - * - * Called by RequestHandle from non-network thread - */ - synchronized void cancel() { - if (HttpLog.LOGV) { - HttpLog.v("Request.cancel(): " + getUri()); - } - - // Ensure that the network thread is not blocked by a hanging request from WebCore to - // pause the load. - mLoadingPaused = false; - notify(); - - mCancelled = true; - if (mConnection != null) { - mConnection.cancel(); - } - } - - String getHostPort() { - String myScheme = mHost.getSchemeName(); - int myPort = mHost.getPort(); - - // Only send port when we must... many servers can't deal with it - if (myPort != 80 && myScheme.equals("http") || - myPort != 443 && myScheme.equals("https")) { - return mHost.toHostString(); - } else { - return mHost.getHostName(); - } - } - - String getUri() { - if (mProxyHost == null || - mHost.getSchemeName().equals("https")) { - return mPath; - } - return mHost.getSchemeName() + "://" + getHostPort() + mPath; - } - - /** - * for debugging - */ - public String toString() { - return mPath; - } - - - /** - * If this request has been sent once and failed, it must be reset - * before it can be sent again. - */ - void reset() { - /* clear content-length header */ - mHttpRequest.removeHeaders(CONTENT_LENGTH_HEADER); - - if (mBodyProvider != null) { - try { - mBodyProvider.reset(); - } catch (IOException ex) { - if (HttpLog.LOGV) HttpLog.v( - "failed to reset body provider " + - getUri()); - } - setBodyProvider(mBodyProvider, mBodyLength); - } - - if (mReceivedBytes > 0) { - // reset the fail count as we continue the request - mFailCount = 0; - // set the "Range" header to indicate that the retry will continue - // instead of restarting the request - HttpLog.v("*** Request.reset() to range:" + mReceivedBytes); - mHttpRequest.setHeader("Range", "bytes=" + mReceivedBytes + "-"); - } - } - - /** - * Pause thread request completes. Used for synchronous requests, - * and testing - */ - void waitUntilComplete() { - synchronized (mClientResource) { - try { - if (HttpLog.LOGV) HttpLog.v("Request.waitUntilComplete()"); - mClientResource.wait(); - if (HttpLog.LOGV) HttpLog.v("Request.waitUntilComplete() done waiting"); - } catch (InterruptedException e) { - } - } - } - - void complete() { - synchronized (mClientResource) { - mClientResource.notifyAll(); - } - } - - /** - * Decide whether a response comes with an entity. - * The implementation in this class is based on RFC 2616. - * Unknown methods and response codes are supposed to - * indicate responses with an entity. - * <br/> - * Derived executors can override this method to handle - * methods and response codes not specified in RFC 2616. - * - * @param request the request, to obtain the executed method - * @param response the response, to obtain the status code - */ - - private static boolean canResponseHaveBody(final HttpRequest request, - final int status) { - - if ("HEAD".equalsIgnoreCase(request.getRequestLine().getMethod())) { - return false; - } - return status >= HttpStatus.SC_OK - && status != HttpStatus.SC_NO_CONTENT - && status != HttpStatus.SC_NOT_MODIFIED; - } - - /** - * Supply an InputStream that provides the body of a request. It's - * not great that the caller must also provide the length of the data - * returned by that InputStream, but the client needs to know up - * front, and I'm not sure how to get this out of the InputStream - * itself without a costly readthrough. I'm not sure skip() would - * do what we want. If you know a better way, please let me know. - */ - private void setBodyProvider(InputStream bodyProvider, int bodyLength) { - if (!bodyProvider.markSupported()) { - throw new IllegalArgumentException( - "bodyProvider must support mark()"); - } - // Mark beginning of stream - bodyProvider.mark(Integer.MAX_VALUE); - - ((BasicHttpEntityEnclosingRequest)mHttpRequest).setEntity( - new InputStreamEntity(bodyProvider, bodyLength)); - } - - - /** - * Handles SSL error(s) on the way down from the user (the user - * has already provided their feedback). - */ - public void handleSslErrorResponse(boolean proceed) { - HttpsConnection connection = (HttpsConnection)(mConnection); - if (connection != null) { - connection.restartConnection(proceed); - } - } - - /** - * Helper: calls error() on eventhandler with appropriate message - * This should not be called before the mConnection is set. - */ - void error(int errorId, int resourceId) { - mEventHandler.error( - errorId, - mConnection.mContext.getText( - resourceId).toString()); - } - -} diff --git a/core/java/android/net/http/RequestFeeder.java b/core/java/android/net/http/RequestFeeder.java deleted file mode 100644 index 34ca267..0000000 --- a/core/java/android/net/http/RequestFeeder.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Supplies Requests to a Connection - */ - -package android.net.http; - -import org.apache.http.HttpHost; - -/** - * {@hide} - */ -interface RequestFeeder { - - Request getRequest(); - Request getRequest(HttpHost host); - - /** - * @return true if a request for this host is available - */ - boolean haveRequest(HttpHost host); - - /** - * Put request back on head of queue - */ - void requeueRequest(Request request); -} diff --git a/core/java/android/net/http/RequestHandle.java b/core/java/android/net/http/RequestHandle.java deleted file mode 100644 index f23f69c..0000000 --- a/core/java/android/net/http/RequestHandle.java +++ /dev/null @@ -1,466 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.http; - -import android.net.ParseException; -import android.net.WebAddress; -import junit.framework.Assert; -import android.webkit.CookieManager; - -import org.apache.commons.codec.binary.Base64; - -import java.io.InputStream; -import java.lang.Math; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.HashMap; -import java.util.Map; -import java.util.Random; - -/** - * RequestHandle: handles a request session that may include multiple - * redirects, HTTP authentication requests, etc. - * - * {@hide} - */ -public class RequestHandle { - - private String mUrl; - private WebAddress mUri; - private String mMethod; - private Map<String, String> mHeaders; - private RequestQueue mRequestQueue; - private Request mRequest; - private InputStream mBodyProvider; - private int mBodyLength; - private int mRedirectCount = 0; - // Used only with synchronous requests. - private Connection mConnection; - - private final static String AUTHORIZATION_HEADER = "Authorization"; - private final static String PROXY_AUTHORIZATION_HEADER = "Proxy-Authorization"; - - public final static int MAX_REDIRECT_COUNT = 16; - - /** - * Creates a new request session. - */ - public RequestHandle(RequestQueue requestQueue, String url, WebAddress uri, - String method, Map<String, String> headers, - InputStream bodyProvider, int bodyLength, Request request) { - - if (headers == null) { - headers = new HashMap<String, String>(); - } - mHeaders = headers; - mBodyProvider = bodyProvider; - mBodyLength = bodyLength; - mMethod = method == null? "GET" : method; - - mUrl = url; - mUri = uri; - - mRequestQueue = requestQueue; - - mRequest = request; - } - - /** - * Creates a new request session with a given Connection. This connection - * is used during a synchronous load to handle this request. - */ - public RequestHandle(RequestQueue requestQueue, String url, WebAddress uri, - String method, Map<String, String> headers, - InputStream bodyProvider, int bodyLength, Request request, - Connection conn) { - this(requestQueue, url, uri, method, headers, bodyProvider, bodyLength, - request); - mConnection = conn; - } - - /** - * Cancels this request - */ - public void cancel() { - if (mRequest != null) { - mRequest.cancel(); - } - } - - /** - * Pauses the loading of this request. For example, called from the WebCore thread - * when the plugin can take no more data. - */ - public void pauseRequest(boolean pause) { - if (mRequest != null) { - mRequest.setLoadingPaused(pause); - } - } - - /** - * Handles SSL error(s) on the way down from the user (the user - * has already provided their feedback). - */ - public void handleSslErrorResponse(boolean proceed) { - if (mRequest != null) { - mRequest.handleSslErrorResponse(proceed); - } - } - - /** - * @return true if we've hit the max redirect count - */ - public boolean isRedirectMax() { - return mRedirectCount >= MAX_REDIRECT_COUNT; - } - - public int getRedirectCount() { - return mRedirectCount; - } - - public void setRedirectCount(int count) { - mRedirectCount = count; - } - - /** - * Create and queue a redirect request. - * - * @param redirectTo URL to redirect to - * @param statusCode HTTP status code returned from original request - * @param cacheHeaders Cache header for redirect URL - * @return true if setup succeeds, false otherwise (redirect loop - * count exceeded, body provider unable to rewind on 307 redirect) - */ - public boolean setupRedirect(String redirectTo, int statusCode, - Map<String, String> cacheHeaders) { - if (HttpLog.LOGV) { - HttpLog.v("RequestHandle.setupRedirect(): redirectCount " + - mRedirectCount); - } - - // be careful and remove authentication headers, if any - mHeaders.remove(AUTHORIZATION_HEADER); - mHeaders.remove(PROXY_AUTHORIZATION_HEADER); - - if (++mRedirectCount == MAX_REDIRECT_COUNT) { - // Way too many redirects -- fail out - if (HttpLog.LOGV) HttpLog.v( - "RequestHandle.setupRedirect(): too many redirects " + - mRequest); - mRequest.error(EventHandler.ERROR_REDIRECT_LOOP, - com.android.internal.R.string.httpErrorRedirectLoop); - return false; - } - - if (mUrl.startsWith("https:") && redirectTo.startsWith("http:")) { - // implement http://www.w3.org/Protocols/rfc2616/rfc2616-sec15.html#sec15.1.3 - if (HttpLog.LOGV) { - HttpLog.v("blowing away the referer on an https -> http redirect"); - } - mHeaders.remove("Referer"); - } - - mUrl = redirectTo; - try { - mUri = new WebAddress(mUrl); - } catch (ParseException e) { - e.printStackTrace(); - } - - // update the "Cookie" header based on the redirected url - mHeaders.remove("Cookie"); - String cookie = CookieManager.getInstance().getCookie(mUri); - if (cookie != null && cookie.length() > 0) { - mHeaders.put("Cookie", cookie); - } - - if ((statusCode == 302 || statusCode == 303) && mMethod.equals("POST")) { - if (HttpLog.LOGV) { - HttpLog.v("replacing POST with GET on redirect to " + redirectTo); - } - mMethod = "GET"; - } - /* Only repost content on a 307. If 307, reset the body - provider so we can replay the body */ - if (statusCode == 307) { - try { - if (mBodyProvider != null) mBodyProvider.reset(); - } catch (java.io.IOException ex) { - if (HttpLog.LOGV) { - HttpLog.v("setupRedirect() failed to reset body provider"); - } - return false; - } - - } else { - mHeaders.remove("Content-Type"); - mBodyProvider = null; - } - - // Update the cache headers for this URL - mHeaders.putAll(cacheHeaders); - - createAndQueueNewRequest(); - return true; - } - - /** - * Create and queue an HTTP authentication-response (basic) request. - */ - public void setupBasicAuthResponse(boolean isProxy, String username, String password) { - String response = computeBasicAuthResponse(username, password); - if (HttpLog.LOGV) { - HttpLog.v("setupBasicAuthResponse(): response: " + response); - } - mHeaders.put(authorizationHeader(isProxy), "Basic " + response); - setupAuthResponse(); - } - - /** - * Create and queue an HTTP authentication-response (digest) request. - */ - public void setupDigestAuthResponse(boolean isProxy, - String username, - String password, - String realm, - String nonce, - String QOP, - String algorithm, - String opaque) { - - String response = computeDigestAuthResponse( - username, password, realm, nonce, QOP, algorithm, opaque); - if (HttpLog.LOGV) { - HttpLog.v("setupDigestAuthResponse(): response: " + response); - } - mHeaders.put(authorizationHeader(isProxy), "Digest " + response); - setupAuthResponse(); - } - - private void setupAuthResponse() { - try { - if (mBodyProvider != null) mBodyProvider.reset(); - } catch (java.io.IOException ex) { - if (HttpLog.LOGV) { - HttpLog.v("setupAuthResponse() failed to reset body provider"); - } - } - createAndQueueNewRequest(); - } - - /** - * @return HTTP request method (GET, PUT, etc). - */ - public String getMethod() { - return mMethod; - } - - /** - * @return Basic-scheme authentication response: BASE64(username:password). - */ - public static String computeBasicAuthResponse(String username, String password) { - Assert.assertNotNull(username); - Assert.assertNotNull(password); - - // encode username:password to base64 - return new String(Base64.encodeBase64((username + ':' + password).getBytes())); - } - - public void waitUntilComplete() { - mRequest.waitUntilComplete(); - } - - public void processRequest() { - if (mConnection != null) { - mConnection.processRequests(mRequest); - } - } - - /** - * @return Digest-scheme authentication response. - */ - private String computeDigestAuthResponse(String username, - String password, - String realm, - String nonce, - String QOP, - String algorithm, - String opaque) { - - Assert.assertNotNull(username); - Assert.assertNotNull(password); - Assert.assertNotNull(realm); - - String A1 = username + ":" + realm + ":" + password; - String A2 = mMethod + ":" + mUrl; - - // because we do not preemptively send authorization headers, nc is always 1 - String nc = "00000001"; - String cnonce = computeCnonce(); - String digest = computeDigest(A1, A2, nonce, QOP, nc, cnonce); - - String response = ""; - response += "username=" + doubleQuote(username) + ", "; - response += "realm=" + doubleQuote(realm) + ", "; - response += "nonce=" + doubleQuote(nonce) + ", "; - response += "uri=" + doubleQuote(mUrl) + ", "; - response += "response=" + doubleQuote(digest) ; - - if (opaque != null) { - response += ", opaque=" + doubleQuote(opaque); - } - - if (algorithm != null) { - response += ", algorithm=" + algorithm; - } - - if (QOP != null) { - response += ", qop=" + QOP + ", nc=" + nc + ", cnonce=" + doubleQuote(cnonce); - } - - return response; - } - - /** - * @return The right authorization header (dependeing on whether it is a proxy or not). - */ - public static String authorizationHeader(boolean isProxy) { - if (!isProxy) { - return AUTHORIZATION_HEADER; - } else { - return PROXY_AUTHORIZATION_HEADER; - } - } - - /** - * @return Double-quoted MD5 digest. - */ - private String computeDigest( - String A1, String A2, String nonce, String QOP, String nc, String cnonce) { - if (HttpLog.LOGV) { - HttpLog.v("computeDigest(): QOP: " + QOP); - } - - if (QOP == null) { - return KD(H(A1), nonce + ":" + H(A2)); - } else { - if (QOP.equalsIgnoreCase("auth")) { - return KD(H(A1), nonce + ":" + nc + ":" + cnonce + ":" + QOP + ":" + H(A2)); - } - } - - return null; - } - - /** - * @return MD5 hash of concat(secret, ":", data). - */ - private String KD(String secret, String data) { - return H(secret + ":" + data); - } - - /** - * @return MD5 hash of param. - */ - private String H(String param) { - if (param != null) { - try { - MessageDigest md5 = MessageDigest.getInstance("MD5"); - - byte[] d = md5.digest(param.getBytes()); - if (d != null) { - return bufferToHex(d); - } - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } - } - - return null; - } - - /** - * @return HEX buffer representation. - */ - private String bufferToHex(byte[] buffer) { - final char hexChars[] = - { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }; - - if (buffer != null) { - int length = buffer.length; - if (length > 0) { - StringBuilder hex = new StringBuilder(2 * length); - - for (int i = 0; i < length; ++i) { - byte l = (byte) (buffer[i] & 0x0F); - byte h = (byte)((buffer[i] & 0xF0) >> 4); - - hex.append(hexChars[h]); - hex.append(hexChars[l]); - } - - return hex.toString(); - } else { - return ""; - } - } - - return null; - } - - /** - * Computes a random cnonce value based on the current time. - */ - private String computeCnonce() { - Random rand = new Random(); - int nextInt = rand.nextInt(); - nextInt = (nextInt == Integer.MIN_VALUE) ? - Integer.MAX_VALUE : Math.abs(nextInt); - return Integer.toString(nextInt, 16); - } - - /** - * "Double-quotes" the argument. - */ - private String doubleQuote(String param) { - if (param != null) { - return "\"" + param + "\""; - } - - return null; - } - - /** - * Creates and queues new request. - */ - private void createAndQueueNewRequest() { - // mConnection is non-null if and only if the requests are synchronous. - if (mConnection != null) { - RequestHandle newHandle = mRequestQueue.queueSynchronousRequest( - mUrl, mUri, mMethod, mHeaders, mRequest.mEventHandler, - mBodyProvider, mBodyLength); - mRequest = newHandle.mRequest; - mConnection = newHandle.mConnection; - newHandle.processRequest(); - return; - } - mRequest = mRequestQueue.queueRequest( - mUrl, mUri, mMethod, mHeaders, mRequest.mEventHandler, - mBodyProvider, - mBodyLength).mRequest; - } -} diff --git a/core/java/android/net/http/RequestQueue.java b/core/java/android/net/http/RequestQueue.java deleted file mode 100644 index 7d2da1b..0000000 --- a/core/java/android/net/http/RequestQueue.java +++ /dev/null @@ -1,542 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * High level HTTP Interface - * Queues requests as necessary - */ - -package android.net.http; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.net.Proxy; -import android.net.WebAddress; -import android.util.Log; - -import java.io.InputStream; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.ListIterator; -import java.util.Map; - -import org.apache.http.HttpHost; - -/** - * {@hide} - */ -public class RequestQueue implements RequestFeeder { - - - /** - * Requests, indexed by HttpHost (scheme, host, port) - */ - private final LinkedHashMap<HttpHost, LinkedList<Request>> mPending; - private final Context mContext; - private final ActivePool mActivePool; - private final ConnectivityManager mConnectivityManager; - - private HttpHost mProxyHost = null; - private BroadcastReceiver mProxyChangeReceiver; - - /* default simultaneous connection count */ - private static final int CONNECTION_COUNT = 4; - - /** - * This class maintains active connection threads - */ - class ActivePool implements ConnectionManager { - /** Threads used to process requests */ - ConnectionThread[] mThreads; - - IdleCache mIdleCache; - - private int mTotalRequest; - private int mTotalConnection; - private int mConnectionCount; - - ActivePool(int connectionCount) { - mIdleCache = new IdleCache(); - mConnectionCount = connectionCount; - mThreads = new ConnectionThread[mConnectionCount]; - - for (int i = 0; i < mConnectionCount; i++) { - mThreads[i] = new ConnectionThread( - mContext, i, this, RequestQueue.this); - } - } - - void startup() { - for (int i = 0; i < mConnectionCount; i++) { - mThreads[i].start(); - } - } - - void shutdown() { - for (int i = 0; i < mConnectionCount; i++) { - mThreads[i].requestStop(); - } - } - - void startConnectionThread() { - synchronized (RequestQueue.this) { - RequestQueue.this.notify(); - } - } - - public void startTiming() { - for (int i = 0; i < mConnectionCount; i++) { - ConnectionThread rt = mThreads[i]; - rt.mCurrentThreadTime = -1; - rt.mTotalThreadTime = 0; - } - mTotalRequest = 0; - mTotalConnection = 0; - } - - public void stopTiming() { - int totalTime = 0; - for (int i = 0; i < mConnectionCount; i++) { - ConnectionThread rt = mThreads[i]; - if (rt.mCurrentThreadTime != -1) { - totalTime += rt.mTotalThreadTime; - } - rt.mCurrentThreadTime = 0; - } - Log.d("Http", "Http thread used " + totalTime + " ms " + " for " - + mTotalRequest + " requests and " + mTotalConnection - + " new connections"); - } - - void logState() { - StringBuilder dump = new StringBuilder(); - for (int i = 0; i < mConnectionCount; i++) { - dump.append(mThreads[i] + "\n"); - } - HttpLog.v(dump.toString()); - } - - - public HttpHost getProxyHost() { - return mProxyHost; - } - - /** - * Turns off persistence on all live connections - */ - void disablePersistence() { - for (int i = 0; i < mConnectionCount; i++) { - Connection connection = mThreads[i].mConnection; - if (connection != null) connection.setCanPersist(false); - } - mIdleCache.clear(); - } - - /* Linear lookup -- okay for small thread counts. Might use - private HashMap<HttpHost, LinkedList<ConnectionThread>> mActiveMap; - if this turns out to be a hotspot */ - ConnectionThread getThread(HttpHost host) { - synchronized(RequestQueue.this) { - for (int i = 0; i < mThreads.length; i++) { - ConnectionThread ct = mThreads[i]; - Connection connection = ct.mConnection; - if (connection != null && connection.mHost.equals(host)) { - return ct; - } - } - } - return null; - } - - public Connection getConnection(Context context, HttpHost host) { - host = RequestQueue.this.determineHost(host); - Connection con = mIdleCache.getConnection(host); - if (con == null) { - mTotalConnection++; - con = Connection.getConnection(mContext, host, mProxyHost, - RequestQueue.this); - } - return con; - } - public boolean recycleConnection(Connection connection) { - return mIdleCache.cacheConnection(connection.getHost(), connection); - } - - } - - /** - * A RequestQueue class instance maintains a set of queued - * requests. It orders them, makes the requests against HTTP - * servers, and makes callbacks to supplied eventHandlers as data - * is read. It supports request prioritization, connection reuse - * and pipelining. - * - * @param context application context - */ - public RequestQueue(Context context) { - this(context, CONNECTION_COUNT); - } - - /** - * A RequestQueue class instance maintains a set of queued - * requests. It orders them, makes the requests against HTTP - * servers, and makes callbacks to supplied eventHandlers as data - * is read. It supports request prioritization, connection reuse - * and pipelining. - * - * @param context application context - * @param connectionCount The number of simultaneous connections - */ - public RequestQueue(Context context, int connectionCount) { - mContext = context; - - mPending = new LinkedHashMap<HttpHost, LinkedList<Request>>(32); - - mActivePool = new ActivePool(connectionCount); - mActivePool.startup(); - - mConnectivityManager = (ConnectivityManager) - context.getSystemService(Context.CONNECTIVITY_SERVICE); - } - - /** - * Enables data state and proxy tracking - */ - public synchronized void enablePlatformNotifications() { - if (HttpLog.LOGV) HttpLog.v("RequestQueue.enablePlatformNotifications() network"); - - if (mProxyChangeReceiver == null) { - mProxyChangeReceiver = - new BroadcastReceiver() { - @Override - public void onReceive(Context ctx, Intent intent) { - setProxyConfig(); - } - }; - mContext.registerReceiver(mProxyChangeReceiver, - new IntentFilter(Proxy.PROXY_CHANGE_ACTION)); - } - // we need to resample the current proxy setup - setProxyConfig(); - } - - /** - * If platform notifications have been enabled, call this method - * to disable before destroying RequestQueue - */ - public synchronized void disablePlatformNotifications() { - if (HttpLog.LOGV) HttpLog.v("RequestQueue.disablePlatformNotifications() network"); - - if (mProxyChangeReceiver != null) { - mContext.unregisterReceiver(mProxyChangeReceiver); - mProxyChangeReceiver = null; - } - } - - /** - * Because our IntentReceiver can run within a different thread, - * synchronize setting the proxy - */ - private synchronized void setProxyConfig() { - NetworkInfo info = mConnectivityManager.getActiveNetworkInfo(); - if (info != null && info.getType() == ConnectivityManager.TYPE_WIFI) { - mProxyHost = null; - } else { - String host = Proxy.getHost(mContext); - if (HttpLog.LOGV) HttpLog.v("RequestQueue.setProxyConfig " + host); - if (host == null) { - mProxyHost = null; - } else { - mActivePool.disablePersistence(); - mProxyHost = new HttpHost(host, Proxy.getPort(mContext), "http"); - } - } - } - - /** - * used by webkit - * @return proxy host if set, null otherwise - */ - public HttpHost getProxyHost() { - return mProxyHost; - } - - /** - * Queues an HTTP request - * @param url The url to load. - * @param method "GET" or "POST." - * @param headers A hashmap of http headers. - * @param eventHandler The event handler for handling returned - * data. Callbacks will be made on the supplied instance. - * @param bodyProvider InputStream providing HTTP body, null if none - * @param bodyLength length of body, must be 0 if bodyProvider is null - */ - public RequestHandle queueRequest( - String url, String method, - Map<String, String> headers, EventHandler eventHandler, - InputStream bodyProvider, int bodyLength) { - WebAddress uri = new WebAddress(url); - return queueRequest(url, uri, method, headers, eventHandler, - bodyProvider, bodyLength); - } - - /** - * Queues an HTTP request - * @param url The url to load. - * @param uri The uri of the url to load. - * @param method "GET" or "POST." - * @param headers A hashmap of http headers. - * @param eventHandler The event handler for handling returned - * data. Callbacks will be made on the supplied instance. - * @param bodyProvider InputStream providing HTTP body, null if none - * @param bodyLength length of body, must be 0 if bodyProvider is null - */ - public RequestHandle queueRequest( - String url, WebAddress uri, String method, Map<String, String> headers, - EventHandler eventHandler, - InputStream bodyProvider, int bodyLength) { - - if (HttpLog.LOGV) HttpLog.v("RequestQueue.queueRequest " + uri); - - // Ensure there is an eventHandler set - if (eventHandler == null) { - eventHandler = new LoggingEventHandler(); - } - - /* Create and queue request */ - Request req; - HttpHost httpHost = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme()); - - // set up request - req = new Request(method, httpHost, mProxyHost, uri.getPath(), bodyProvider, - bodyLength, eventHandler, headers); - - queueRequest(req, false); - - mActivePool.mTotalRequest++; - - // dump(); - mActivePool.startConnectionThread(); - - return new RequestHandle( - this, url, uri, method, headers, bodyProvider, bodyLength, - req); - } - - private static class SyncFeeder implements RequestFeeder { - // This is used in the case where the request fails and needs to be - // requeued into the RequestFeeder. - private Request mRequest; - SyncFeeder() { - } - public Request getRequest() { - Request r = mRequest; - mRequest = null; - return r; - } - public Request getRequest(HttpHost host) { - return getRequest(); - } - public boolean haveRequest(HttpHost host) { - return mRequest != null; - } - public void requeueRequest(Request r) { - mRequest = r; - } - } - - public RequestHandle queueSynchronousRequest(String url, WebAddress uri, - String method, Map<String, String> headers, - EventHandler eventHandler, InputStream bodyProvider, - int bodyLength) { - if (HttpLog.LOGV) { - HttpLog.v("RequestQueue.dispatchSynchronousRequest " + uri); - } - - HttpHost host = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme()); - - Request req = new Request(method, host, mProxyHost, uri.getPath(), - bodyProvider, bodyLength, eventHandler, headers); - - // Open a new connection that uses our special RequestFeeder - // implementation. - host = determineHost(host); - Connection conn = Connection.getConnection(mContext, host, mProxyHost, - new SyncFeeder()); - - // TODO: I would like to process the request here but LoadListener - // needs a RequestHandle to process some messages. - return new RequestHandle(this, url, uri, method, headers, bodyProvider, - bodyLength, req, conn); - - } - - // Chooses between the proxy and the request's host. - private HttpHost determineHost(HttpHost host) { - // There used to be a comment in ConnectionThread about t-mob's proxy - // being really bad about https. But, HttpsConnection actually looks - // for a proxy and connects through it anyway. I think that this check - // is still valid because if a site is https, we will use - // HttpsConnection rather than HttpConnection if the proxy address is - // not secure. - return (mProxyHost == null || "https".equals(host.getSchemeName())) - ? host : mProxyHost; - } - - /** - * @return true iff there are any non-active requests pending - */ - synchronized boolean requestsPending() { - return !mPending.isEmpty(); - } - - - /** - * debug tool: prints request queue to log - */ - synchronized void dump() { - HttpLog.v("dump()"); - StringBuilder dump = new StringBuilder(); - int count = 0; - Iterator<Map.Entry<HttpHost, LinkedList<Request>>> iter; - - // mActivePool.log(dump); - - if (!mPending.isEmpty()) { - iter = mPending.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry<HttpHost, LinkedList<Request>> entry = iter.next(); - String hostName = entry.getKey().getHostName(); - StringBuilder line = new StringBuilder("p" + count++ + " " + hostName + " "); - - LinkedList<Request> reqList = entry.getValue(); - ListIterator reqIter = reqList.listIterator(0); - while (iter.hasNext()) { - Request request = (Request)iter.next(); - line.append(request + " "); - } - dump.append(line); - dump.append("\n"); - } - } - HttpLog.v(dump.toString()); - } - - /* - * RequestFeeder implementation - */ - public synchronized Request getRequest() { - Request ret = null; - - if (!mPending.isEmpty()) { - ret = removeFirst(mPending); - } - if (HttpLog.LOGV) HttpLog.v("RequestQueue.getRequest() => " + ret); - return ret; - } - - /** - * @return a request for given host if possible - */ - public synchronized Request getRequest(HttpHost host) { - Request ret = null; - - if (mPending.containsKey(host)) { - LinkedList<Request> reqList = mPending.get(host); - ret = reqList.removeFirst(); - if (reqList.isEmpty()) { - mPending.remove(host); - } - } - if (HttpLog.LOGV) HttpLog.v("RequestQueue.getRequest(" + host + ") => " + ret); - return ret; - } - - /** - * @return true if a request for this host is available - */ - public synchronized boolean haveRequest(HttpHost host) { - return mPending.containsKey(host); - } - - /** - * Put request back on head of queue - */ - public void requeueRequest(Request request) { - queueRequest(request, true); - } - - /** - * This must be called to cleanly shutdown RequestQueue - */ - public void shutdown() { - mActivePool.shutdown(); - } - - protected synchronized void queueRequest(Request request, boolean head) { - HttpHost host = request.mProxyHost == null ? request.mHost : request.mProxyHost; - LinkedList<Request> reqList; - if (mPending.containsKey(host)) { - reqList = mPending.get(host); - } else { - reqList = new LinkedList<Request>(); - mPending.put(host, reqList); - } - if (head) { - reqList.addFirst(request); - } else { - reqList.add(request); - } - } - - - public void startTiming() { - mActivePool.startTiming(); - } - - public void stopTiming() { - mActivePool.stopTiming(); - } - - /* helper */ - private Request removeFirst(LinkedHashMap<HttpHost, LinkedList<Request>> requestQueue) { - Request ret = null; - Iterator<Map.Entry<HttpHost, LinkedList<Request>>> iter = requestQueue.entrySet().iterator(); - if (iter.hasNext()) { - Map.Entry<HttpHost, LinkedList<Request>> entry = iter.next(); - LinkedList<Request> reqList = entry.getValue(); - ret = reqList.removeFirst(); - if (reqList.isEmpty()) { - requestQueue.remove(entry.getKey()); - } - } - return ret; - } - - /** - * This interface is exposed to each connection - */ - interface ConnectionManager { - HttpHost getProxyHost(); - Connection getConnection(Context context, HttpHost host); - boolean recycleConnection(Connection connection); - } -} diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 250e80f..15acaad 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -51,14 +51,21 @@ import android.os.Build.VERSION_CODES; import android.speech.tts.TextToSpeech; import android.text.TextUtils; import android.util.AndroidException; +import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Log; +import com.android.internal.util.ArrayUtils; import com.android.internal.widget.ILockSettings; import java.net.URISyntaxException; +import java.text.SimpleDateFormat; import java.util.HashMap; import java.util.HashSet; import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; /** * The Settings provider contains global system-level device preferences. @@ -1192,6 +1199,11 @@ public final class Settings { public static final class System extends NameValueTable { public static final String SYS_PROP_SETTING_VERSION = "sys.settings_system_version"; + /** @hide */ + public static interface Validator { + public boolean validate(String value); + } + /** * The content:// style URL for this table */ @@ -1294,13 +1306,56 @@ public final class Settings { MOVED_TO_GLOBAL.add(Settings.Global.CERT_PIN_UPDATE_METADATA_URL); } + private static final Validator sBooleanValidator = + new DiscreteValueValidator(new String[] {"0", "1"}); + + private static final Validator sNonNegativeIntegerValidator = new Validator() { + @Override + public boolean validate(String value) { + try { + return Integer.parseInt(value) >= 0; + } catch (NumberFormatException e) { + return false; + } + } + }; + + private static final Validator sVolumeValidator = + new InclusiveFloatRangeValidator(0, 1); + + private static final Validator sUriValidator = new Validator() { + @Override + public boolean validate(String value) { + try { + Uri.decode(value); + return true; + } catch (IllegalArgumentException e) { + return false; + } + } + }; + + private static final Validator sLenientIpAddressValidator = new Validator() { + private static final int MAX_IPV6_LENGTH = 45; + + @Override + public boolean validate(String value) { + return value.length() <= MAX_IPV6_LENGTH; + } + }; + /** @hide */ - public static void getMovedKeys(HashSet<String> outKeySet) { + public static void getMovedToGlobalSettings(Set<String> outKeySet) { outKeySet.addAll(MOVED_TO_GLOBAL); outKeySet.addAll(MOVED_TO_SECURE_THEN_GLOBAL); } /** @hide */ + public static void getMovedToSecureSettings(Set<String> outKeySet) { + outKeySet.addAll(MOVED_TO_SECURE); + } + + /** @hide */ public static void getNonLegacyMovedKeys(HashSet<String> outKeySet) { outKeySet.addAll(MOVED_TO_GLOBAL); } @@ -1723,6 +1778,56 @@ public final class Settings { putIntForUser(cr, SHOW_GTALK_SERVICE_STATUS, flag ? 1 : 0, userHandle); } + private static final class DiscreteValueValidator implements Validator { + private final String[] mValues; + + public DiscreteValueValidator(String[] values) { + mValues = values; + } + + public boolean validate(String value) { + return ArrayUtils.contains(mValues, value); + } + } + + private static final class InclusiveIntegerRangeValidator implements Validator { + private final int mMin; + private final int mMax; + + public InclusiveIntegerRangeValidator(int min, int max) { + mMin = min; + mMax = max; + } + + public boolean validate(String value) { + try { + final int intValue = Integer.parseInt(value); + return intValue >= mMin && intValue <= mMax; + } catch (NumberFormatException e) { + return false; + } + } + } + + private static final class InclusiveFloatRangeValidator implements Validator { + private final float mMin; + private final float mMax; + + public InclusiveFloatRangeValidator(float min, float max) { + mMin = min; + mMax = max; + } + + public boolean validate(String value) { + try { + final float floatValue = Float.parseFloat(value); + return floatValue >= mMin && floatValue <= mMax; + } catch (NumberFormatException e) { + return false; + } + } + } + /** * @deprecated Use {@link android.provider.Settings.Global#STAY_ON_WHILE_PLUGGED_IN} instead */ @@ -1741,6 +1846,9 @@ public final class Settings { */ public static final String END_BUTTON_BEHAVIOR = "end_button_behavior"; + private static final Validator END_BUTTON_BEHAVIOR_VALIDATOR = + new InclusiveIntegerRangeValidator(0, 3); + /** * END_BUTTON_BEHAVIOR value for "go home". * @hide @@ -1765,6 +1873,8 @@ public final class Settings { */ public static final String ADVANCED_SETTINGS = "advanced_settings"; + private static final Validator ADVANCED_SETTINGS_VALIDATOR = sBooleanValidator; + /** * ADVANCED_SETTINGS default value. * @hide @@ -1864,6 +1974,8 @@ public final class Settings { @Deprecated public static final String WIFI_USE_STATIC_IP = "wifi_use_static_ip"; + private static final Validator WIFI_USE_STATIC_IP_VALIDATOR = sBooleanValidator; + /** * The static IP address. * <p> @@ -1874,6 +1986,8 @@ public final class Settings { @Deprecated public static final String WIFI_STATIC_IP = "wifi_static_ip"; + private static final Validator WIFI_STATIC_IP_VALIDATOR = sLenientIpAddressValidator; + /** * If using static IP, the gateway's IP address. * <p> @@ -1884,6 +1998,8 @@ public final class Settings { @Deprecated public static final String WIFI_STATIC_GATEWAY = "wifi_static_gateway"; + private static final Validator WIFI_STATIC_GATEWAY_VALIDATOR = sLenientIpAddressValidator; + /** * If using static IP, the net mask. * <p> @@ -1894,6 +2010,8 @@ public final class Settings { @Deprecated public static final String WIFI_STATIC_NETMASK = "wifi_static_netmask"; + private static final Validator WIFI_STATIC_NETMASK_VALIDATOR = sLenientIpAddressValidator; + /** * If using static IP, the primary DNS's IP address. * <p> @@ -1904,6 +2022,8 @@ public final class Settings { @Deprecated public static final String WIFI_STATIC_DNS1 = "wifi_static_dns1"; + private static final Validator WIFI_STATIC_DNS1_VALIDATOR = sLenientIpAddressValidator; + /** * If using static IP, the secondary DNS's IP address. * <p> @@ -1914,6 +2034,7 @@ public final class Settings { @Deprecated public static final String WIFI_STATIC_DNS2 = "wifi_static_dns2"; + private static final Validator WIFI_STATIC_DNS2_VALIDATOR = sLenientIpAddressValidator; /** * Determines whether remote devices may discover and/or connect to @@ -1926,6 +2047,9 @@ public final class Settings { public static final String BLUETOOTH_DISCOVERABILITY = "bluetooth_discoverability"; + private static final Validator BLUETOOTH_DISCOVERABILITY_VALIDATOR = + new InclusiveIntegerRangeValidator(0, 2); + /** * Bluetooth discoverability timeout. If this value is nonzero, then * Bluetooth becomes discoverable for a certain number of seconds, @@ -1934,6 +2058,9 @@ public final class Settings { public static final String BLUETOOTH_DISCOVERABILITY_TIMEOUT = "bluetooth_discoverability_timeout"; + private static final Validator BLUETOOTH_DISCOVERABILITY_TIMEOUT_VALIDATOR = + sNonNegativeIntegerValidator; + /** * @deprecated Use {@link android.provider.Settings.Secure#LOCK_PATTERN_ENABLED} * instead @@ -1957,7 +2084,6 @@ public final class Settings { public static final String LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = "lock_pattern_tactile_feedback_enabled"; - /** * A formatted string of the next alarm that is set, or the empty string * if there is no alarm set. @@ -1967,11 +2093,31 @@ public final class Settings { @Deprecated public static final String NEXT_ALARM_FORMATTED = "next_alarm_formatted"; + private static final Validator NEXT_ALARM_FORMATTED_VALIDATOR = new Validator() { + private static final int MAX_LENGTH = 1000; + @Override + public boolean validate(String value) { + // TODO: No idea what the correct format is. + return value == null || value.length() < MAX_LENGTH; + } + }; + /** * Scaling factor for fonts, float. */ public static final String FONT_SCALE = "font_scale"; + private static final Validator FONT_SCALE_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + try { + return Float.parseFloat(value) >= 0; + } catch (NumberFormatException e) { + return false; + } + } + }; + /** * Name of an application package to be debugged. * @@ -1996,6 +2142,8 @@ public final class Settings { @Deprecated public static final String DIM_SCREEN = "dim_screen"; + private static final Validator DIM_SCREEN_VALIDATOR = sBooleanValidator; + /** * The amount of time in milliseconds before the device goes to sleep or begins * to dream after a period of inactivity. This value is also known as the @@ -2004,16 +2152,23 @@ public final class Settings { */ public static final String SCREEN_OFF_TIMEOUT = "screen_off_timeout"; + private static final Validator SCREEN_OFF_TIMEOUT_VALIDATOR = sNonNegativeIntegerValidator; + /** * The screen backlight brightness between 0 and 255. */ public static final String SCREEN_BRIGHTNESS = "screen_brightness"; + private static final Validator SCREEN_BRIGHTNESS_VALIDATOR = + new InclusiveIntegerRangeValidator(0, 255); + /** * Control whether to enable automatic brightness mode. */ public static final String SCREEN_BRIGHTNESS_MODE = "screen_brightness_mode"; + private static final Validator SCREEN_BRIGHTNESS_MODE_VALIDATOR = sBooleanValidator; + /** * Adjustment to auto-brightness to make it generally more (>0.0 <1.0) * or less (<0.0 >-1.0) bright. @@ -2021,6 +2176,9 @@ public final class Settings { */ public static final String SCREEN_AUTO_BRIGHTNESS_ADJ = "screen_auto_brightness_adj"; + private static final Validator SCREEN_AUTO_BRIGHTNESS_ADJ_VALIDATOR = + new InclusiveFloatRangeValidator(-1, 1); + /** * SCREEN_BRIGHTNESS_MODE value for manual mode. */ @@ -2056,12 +2214,18 @@ public final class Settings { */ public static final String MODE_RINGER_STREAMS_AFFECTED = "mode_ringer_streams_affected"; - /** + private static final Validator MODE_RINGER_STREAMS_AFFECTED_VALIDATOR = + sNonNegativeIntegerValidator; + + /** * Determines which streams are affected by mute. The * stream type's bit should be set to 1 if it should be muted when a mute request * is received. */ - public static final String MUTE_STREAMS_AFFECTED = "mute_streams_affected"; + public static final String MUTE_STREAMS_AFFECTED = "mute_streams_affected"; + + private static final Validator MUTE_STREAMS_AFFECTED_VALIDATOR = + sNonNegativeIntegerValidator; /** * Whether vibrate is on for different events. This is used internally, @@ -2069,6 +2233,8 @@ public final class Settings { */ public static final String VIBRATE_ON = "vibrate_on"; + private static final Validator VIBRATE_ON_VALIDATOR = sBooleanValidator; + /** * If 1, redirects the system vibrator to all currently attached input devices * that support vibration. If there are no such input devices, then the system @@ -2083,54 +2249,72 @@ public final class Settings { */ public static final String VIBRATE_INPUT_DEVICES = "vibrate_input_devices"; + private static final Validator VIBRATE_INPUT_DEVICES_VALIDATOR = sBooleanValidator; + /** * Ringer volume. This is used internally, changing this value will not * change the volume. See AudioManager. */ public static final String VOLUME_RING = "volume_ring"; + private static final Validator VOLUME_RING_VALIDATOR = sVolumeValidator; + /** * System/notifications volume. This is used internally, changing this * value will not change the volume. See AudioManager. */ public static final String VOLUME_SYSTEM = "volume_system"; + private static final Validator VOLUME_SYSTEM_VALIDATOR = sVolumeValidator; + /** * Voice call volume. This is used internally, changing this value will * not change the volume. See AudioManager. */ public static final String VOLUME_VOICE = "volume_voice"; + private static final Validator VOLUME_VOICE_VALIDATOR = sVolumeValidator; + /** * Music/media/gaming volume. This is used internally, changing this * value will not change the volume. See AudioManager. */ public static final String VOLUME_MUSIC = "volume_music"; + private static final Validator VOLUME_MUSIC_VALIDATOR = sVolumeValidator; + /** * Alarm volume. This is used internally, changing this * value will not change the volume. See AudioManager. */ public static final String VOLUME_ALARM = "volume_alarm"; + private static final Validator VOLUME_ALARM_VALIDATOR = sVolumeValidator; + /** * Notification volume. This is used internally, changing this * value will not change the volume. See AudioManager. */ public static final String VOLUME_NOTIFICATION = "volume_notification"; + private static final Validator VOLUME_NOTIFICATION_VALIDATOR = sVolumeValidator; + /** * Bluetooth Headset volume. This is used internally, changing this value will * not change the volume. See AudioManager. */ public static final String VOLUME_BLUETOOTH_SCO = "volume_bluetooth_sco"; + private static final Validator VOLUME_BLUETOOTH_SCO_VALIDATOR = sVolumeValidator; + /** * Master volume (float in the range 0.0f to 1.0f). * @hide */ public static final String VOLUME_MASTER = "volume_master"; + private static final Validator VOLUME_MASTER_VALIDATOR = sVolumeValidator; + /** * Master volume mute (int 1 = mute, 0 = not muted). * @@ -2138,6 +2322,8 @@ public final class Settings { */ public static final String VOLUME_MASTER_MUTE = "volume_master_mute"; + private static final Validator VOLUME_MASTER_MUTE_VALIDATOR = sBooleanValidator; + /** * Microphone mute (int 1 = mute, 0 = not muted). * @@ -2145,6 +2331,8 @@ public final class Settings { */ public static final String MICROPHONE_MUTE = "microphone_mute"; + private static final Validator MICROPHONE_MUTE_VALIDATOR = sBooleanValidator; + /** * Whether the notifications should use the ring volume (value of 1) or * a separate notification volume (value of 0). In most cases, users @@ -2163,6 +2351,8 @@ public final class Settings { public static final String NOTIFICATIONS_USE_RING_VOLUME = "notifications_use_ring_volume"; + private static final Validator NOTIFICATIONS_USE_RING_VOLUME_VALIDATOR = sBooleanValidator; + /** * Whether silent mode should allow vibration feedback. This is used * internally in AudioService and the Sound settings activity to @@ -2177,6 +2367,8 @@ public final class Settings { */ public static final String VIBRATE_IN_SILENT = "vibrate_in_silent"; + private static final Validator VIBRATE_IN_SILENT_VALIDATOR = sBooleanValidator; + /** * The mapping of stream type (integer) to its setting. */ @@ -2203,6 +2395,8 @@ public final class Settings { */ public static final String RINGTONE = "ringtone"; + private static final Validator RINGTONE_VALIDATOR = sUriValidator; + /** * A {@link Uri} that will point to the current default ringtone at any * given time. @@ -2221,6 +2415,8 @@ public final class Settings { */ public static final String NOTIFICATION_SOUND = "notification_sound"; + private static final Validator NOTIFICATION_SOUND_VALIDATOR = sUriValidator; + /** * A {@link Uri} that will point to the current default notification * sound at any given time. @@ -2237,6 +2433,8 @@ public final class Settings { */ public static final String ALARM_ALERT = "alarm_alert"; + private static final Validator ALARM_ALERT_VALIDATOR = sUriValidator; + /** * A {@link Uri} that will point to the current default alarm alert at * any given time. @@ -2252,30 +2450,52 @@ public final class Settings { */ public static final String MEDIA_BUTTON_RECEIVER = "media_button_receiver"; + private static final Validator MEDIA_BUTTON_RECEIVER_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + try { + ComponentName.unflattenFromString(value); + return true; + } catch (NullPointerException e) { + return false; + } + } + }; + /** * Setting to enable Auto Replace (AutoText) in text editors. 1 = On, 0 = Off */ public static final String TEXT_AUTO_REPLACE = "auto_replace"; + private static final Validator TEXT_AUTO_REPLACE_VALIDATOR = sBooleanValidator; + /** * Setting to enable Auto Caps in text editors. 1 = On, 0 = Off */ public static final String TEXT_AUTO_CAPS = "auto_caps"; + private static final Validator TEXT_AUTO_CAPS_VALIDATOR = sBooleanValidator; + /** * Setting to enable Auto Punctuate in text editors. 1 = On, 0 = Off. This * feature converts two spaces to a "." and space. */ public static final String TEXT_AUTO_PUNCTUATE = "auto_punctuate"; + private static final Validator TEXT_AUTO_PUNCTUATE_VALIDATOR = sBooleanValidator; + /** * Setting to showing password characters in text editors. 1 = On, 0 = Off */ public static final String TEXT_SHOW_PASSWORD = "show_password"; + private static final Validator TEXT_SHOW_PASSWORD_VALIDATOR = sBooleanValidator; + public static final String SHOW_GTALK_SERVICE_STATUS = "SHOW_GTALK_SERVICE_STATUS"; + private static final Validator SHOW_GTALK_SERVICE_STATUS_VALIDATOR = sBooleanValidator; + /** * Name of activity to use for wallpaper on the home screen. * @@ -2284,6 +2504,18 @@ public final class Settings { @Deprecated public static final String WALLPAPER_ACTIVITY = "wallpaper_activity"; + private static final Validator WALLPAPER_ACTIVITY_VALIDATOR = new Validator() { + private static final int MAX_LENGTH = 1000; + + @Override + public boolean validate(String value) { + if (value != null && value.length() > MAX_LENGTH) { + return false; + } + return ComponentName.unflattenFromString(value) != null; + } + }; + /** * @deprecated Use {@link android.provider.Settings.Global#AUTO_TIME} * instead @@ -2305,6 +2537,10 @@ public final class Settings { */ public static final String TIME_12_24 = "time_12_24"; + /** @hide */ + public static final Validator TIME_12_24_VALIDATOR = + new DiscreteValueValidator(new String[] {"12", "24"}); + /** * Date format string * mm/dd/yyyy @@ -2313,6 +2549,19 @@ public final class Settings { */ public static final String DATE_FORMAT = "date_format"; + /** @hide */ + public static final Validator DATE_FORMAT_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + try { + new SimpleDateFormat(value); + return true; + } catch (IllegalArgumentException e) { + return false; + } + } + }; + /** * Whether the setup wizard has been run before (on first boot), or if * it still needs to be run. @@ -2322,6 +2571,9 @@ public final class Settings { */ public static final String SETUP_WIZARD_HAS_RUN = "setup_wizard_has_run"; + /** @hide */ + public static final Validator SETUP_WIZARD_HAS_RUN_VALIDATOR = sBooleanValidator; + /** * Scaling factor for normal window animations. Setting to 0 will disable window * animations. @@ -2358,6 +2610,9 @@ public final class Settings { */ public static final String ACCELEROMETER_ROTATION = "accelerometer_rotation"; + /** @hide */ + public static final Validator ACCELEROMETER_ROTATION_VALIDATOR = sBooleanValidator; + /** * Default screen rotation when no other policy applies. * When {@link #ACCELEROMETER_ROTATION} is zero and no on-screen Activity expresses a @@ -2368,6 +2623,10 @@ public final class Settings { */ public static final String USER_ROTATION = "user_rotation"; + /** @hide */ + public static final Validator USER_ROTATION_VALIDATOR = + new InclusiveIntegerRangeValidator(0, 3); + /** * Control whether the rotation lock toggle in the System UI should be hidden. * Typically this is done for accessibility purposes to make it harder for @@ -2382,6 +2641,10 @@ public final class Settings { public static final String HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY = "hide_rotation_lock_toggle_for_accessibility"; + /** @hide */ + public static final Validator HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY_VALIDATOR = + sBooleanValidator; + /** * Whether the phone vibrates when it is ringing due to an incoming call. This will * be used by Phone and Setting apps; it shouldn't affect other apps. @@ -2396,12 +2659,18 @@ public final class Settings { */ public static final String VIBRATE_WHEN_RINGING = "vibrate_when_ringing"; + /** @hide */ + public static final Validator VIBRATE_WHEN_RINGING_VALIDATOR = sBooleanValidator; + /** * Whether the audible DTMF tones are played by the dialer when dialing. The value is * boolean (1 or 0). */ public static final String DTMF_TONE_WHEN_DIALING = "dtmf_tone"; + /** @hide */ + public static final Validator DTMF_TONE_WHEN_DIALING_VALIDATOR = sBooleanValidator; + /** * CDMA only settings * DTMF tone type played by the dialer when dialing. @@ -2411,6 +2680,9 @@ public final class Settings { */ public static final String DTMF_TONE_TYPE_WHEN_DIALING = "dtmf_tone_type"; + /** @hide */ + public static final Validator DTMF_TONE_TYPE_WHEN_DIALING_VALIDATOR = sBooleanValidator; + /** * Whether the hearing aid is enabled. The value is * boolean (1 or 0). @@ -2418,6 +2690,9 @@ public final class Settings { */ public static final String HEARING_AID = "hearing_aid"; + /** @hide */ + public static final Validator HEARING_AID_VALIDATOR = sBooleanValidator; + /** * CDMA only settings * TTY Mode @@ -2429,18 +2704,27 @@ public final class Settings { */ public static final String TTY_MODE = "tty_mode"; + /** @hide */ + public static final Validator TTY_MODE_VALIDATOR = new InclusiveIntegerRangeValidator(0, 3); + /** * Whether the sounds effects (key clicks, lid open ...) are enabled. The value is * boolean (1 or 0). */ public static final String SOUND_EFFECTS_ENABLED = "sound_effects_enabled"; + /** @hide */ + public static final Validator SOUND_EFFECTS_ENABLED_VALIDATOR = sBooleanValidator; + /** * Whether the haptic feedback (long presses, ...) are enabled. The value is * boolean (1 or 0). */ public static final String HAPTIC_FEEDBACK_ENABLED = "haptic_feedback_enabled"; + /** @hide */ + public static final Validator HAPTIC_FEEDBACK_ENABLED_VALIDATOR = sBooleanValidator; + /** * @deprecated Each application that shows web suggestions should have its own * setting for this. @@ -2448,6 +2732,9 @@ public final class Settings { @Deprecated public static final String SHOW_WEB_SUGGESTIONS = "show_web_suggestions"; + /** @hide */ + public static final Validator SHOW_WEB_SUGGESTIONS_VALIDATOR = sBooleanValidator; + /** * Whether the notification LED should repeatedly flash when a notification is * pending. The value is boolean (1 or 0). @@ -2455,6 +2742,9 @@ public final class Settings { */ public static final String NOTIFICATION_LIGHT_PULSE = "notification_light_pulse"; + /** @hide */ + public static final Validator NOTIFICATION_LIGHT_PULSE_VALIDATOR = sBooleanValidator; + /** * Show pointer location on screen? * 0 = no @@ -2463,6 +2753,9 @@ public final class Settings { */ public static final String POINTER_LOCATION = "pointer_location"; + /** @hide */ + public static final Validator POINTER_LOCATION_VALIDATOR = sBooleanValidator; + /** * Show touch positions on screen? * 0 = no @@ -2471,6 +2764,9 @@ public final class Settings { */ public static final String SHOW_TOUCHES = "show_touches"; + /** @hide */ + public static final Validator SHOW_TOUCHES_VALIDATOR = sBooleanValidator; + /** * Log raw orientation data from * {@link com.android.server.policy.WindowOrientationListener} for use with the @@ -2482,6 +2778,9 @@ public final class Settings { public static final String WINDOW_ORIENTATION_LISTENER_LOG = "window_orientation_listener_log"; + /** @hide */ + public static final Validator WINDOW_ORIENTATION_LISTENER_LOG_VALIDATOR = sBooleanValidator; + /** * @deprecated Use {@link android.provider.Settings.Global#POWER_SOUNDS_ENABLED} * instead @@ -2504,12 +2803,18 @@ public final class Settings { */ public static final String LOCKSCREEN_SOUNDS_ENABLED = "lockscreen_sounds_enabled"; + /** @hide */ + public static final Validator LOCKSCREEN_SOUNDS_ENABLED_VALIDATOR = sBooleanValidator; + /** * Whether the lockscreen should be completely disabled. * @hide */ public static final String LOCKSCREEN_DISABLED = "lockscreen.disabled"; + /** @hide */ + public static final Validator LOCKSCREEN_DISABLED_VALIDATOR = sBooleanValidator; + /** * @deprecated Use {@link android.provider.Settings.Global#LOW_BATTERY_SOUND} * instead @@ -2574,6 +2879,9 @@ public final class Settings { */ public static final String SIP_RECEIVE_CALLS = "sip_receive_calls"; + /** @hide */ + public static final Validator SIP_RECEIVE_CALLS_VALIDATOR = sBooleanValidator; + /** * Call Preference String. * "SIP_ALWAYS" : Always use SIP with network access @@ -2582,18 +2890,28 @@ public final class Settings { */ public static final String SIP_CALL_OPTIONS = "sip_call_options"; + /** @hide */ + public static final Validator SIP_CALL_OPTIONS_VALIDATOR = new DiscreteValueValidator( + new String[] {"SIP_ALWAYS", "SIP_ADDRESS_ONLY"}); + /** * One of the sip call options: Always use SIP with network access. * @hide */ public static final String SIP_ALWAYS = "SIP_ALWAYS"; + /** @hide */ + public static final Validator SIP_ALWAYS_VALIDATOR = sBooleanValidator; + /** * One of the sip call options: Only if destination is a SIP address. * @hide */ public static final String SIP_ADDRESS_ONLY = "SIP_ADDRESS_ONLY"; + /** @hide */ + public static final Validator SIP_ADDRESS_ONLY_VALIDATOR = sBooleanValidator; + /** * @deprecated Use SIP_ALWAYS or SIP_ADDRESS_ONLY instead. Formerly used to indicate that * the user should be prompted each time a call is made whether it should be placed using @@ -2604,6 +2922,9 @@ public final class Settings { @Deprecated public static final String SIP_ASK_ME_EACH_TIME = "SIP_ASK_ME_EACH_TIME"; + /** @hide */ + public static final Validator SIP_ASK_ME_EACH_TIME_VALIDATOR = sBooleanValidator; + /** * Pointer speed setting. * This is an integer value in a range between -7 and +7, so there are 15 possible values. @@ -2614,12 +2935,19 @@ public final class Settings { */ public static final String POINTER_SPEED = "pointer_speed"; + /** @hide */ + public static final Validator POINTER_SPEED_VALIDATOR = + new InclusiveFloatRangeValidator(-7, 7); + /** * Whether lock-to-app will be triggered by long-press on recents. * @hide */ public static final String LOCK_TO_APP_ENABLED = "lock_to_app_enabled"; + /** @hide */ + public static final Validator LOCK_TO_APP_ENABLED_VALIDATOR = sBooleanValidator; + /** * I am the lolrus. * <p> @@ -2629,6 +2957,16 @@ public final class Settings { */ public static final String EGG_MODE = "egg_mode"; + /** @hide */ + public static final Validator EGG_MODE_VALIDATOR = sBooleanValidator; + + /** + * IMPORTANT: If you add a new public settings you also have to add it to + * PUBLIC_SETTINGS below. If the new setting is hidden you have to add + * it to PRIVATE_SETTINGS below. Also add a validator that can validate + * the setting value. See an example above. + */ + /** * Settings to backup. This is here so that it's in the same place as the settings * keys and easy to update. @@ -2699,17 +3037,207 @@ public final class Settings { }; /** + * These are all pulbic system settings + * + * @hide + */ + public static final Set<String> PUBLIC_SETTINGS = new ArraySet<>(); + static { + PUBLIC_SETTINGS.add(END_BUTTON_BEHAVIOR); + PUBLIC_SETTINGS.add(WIFI_USE_STATIC_IP); + PUBLIC_SETTINGS.add(WIFI_STATIC_IP); + PUBLIC_SETTINGS.add(WIFI_STATIC_GATEWAY); + PUBLIC_SETTINGS.add(WIFI_STATIC_NETMASK); + PUBLIC_SETTINGS.add(WIFI_STATIC_DNS1); + PUBLIC_SETTINGS.add(WIFI_STATIC_DNS2); + PUBLIC_SETTINGS.add(BLUETOOTH_DISCOVERABILITY); + PUBLIC_SETTINGS.add(BLUETOOTH_DISCOVERABILITY_TIMEOUT); + PUBLIC_SETTINGS.add(NEXT_ALARM_FORMATTED); + PUBLIC_SETTINGS.add(FONT_SCALE); + PUBLIC_SETTINGS.add(DIM_SCREEN); + PUBLIC_SETTINGS.add(SCREEN_OFF_TIMEOUT); + PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS); + PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS_MODE); + PUBLIC_SETTINGS.add(MODE_RINGER_STREAMS_AFFECTED); + PUBLIC_SETTINGS.add(MUTE_STREAMS_AFFECTED); + PUBLIC_SETTINGS.add(VIBRATE_ON); + PUBLIC_SETTINGS.add(VOLUME_RING); + PUBLIC_SETTINGS.add(VOLUME_SYSTEM); + PUBLIC_SETTINGS.add(VOLUME_VOICE); + PUBLIC_SETTINGS.add(VOLUME_MUSIC); + PUBLIC_SETTINGS.add(VOLUME_ALARM); + PUBLIC_SETTINGS.add(VOLUME_NOTIFICATION); + PUBLIC_SETTINGS.add(VOLUME_BLUETOOTH_SCO); + PUBLIC_SETTINGS.add(RINGTONE); + PUBLIC_SETTINGS.add(NOTIFICATION_SOUND); + PUBLIC_SETTINGS.add(ALARM_ALERT); + PUBLIC_SETTINGS.add(TEXT_AUTO_REPLACE); + PUBLIC_SETTINGS.add(TEXT_AUTO_CAPS); + PUBLIC_SETTINGS.add(TEXT_AUTO_PUNCTUATE); + PUBLIC_SETTINGS.add(TEXT_SHOW_PASSWORD); + PUBLIC_SETTINGS.add(SHOW_GTALK_SERVICE_STATUS); + PUBLIC_SETTINGS.add(WALLPAPER_ACTIVITY); + PUBLIC_SETTINGS.add(TIME_12_24); + PUBLIC_SETTINGS.add(DATE_FORMAT); + PUBLIC_SETTINGS.add(SETUP_WIZARD_HAS_RUN); + PUBLIC_SETTINGS.add(ACCELEROMETER_ROTATION); + PUBLIC_SETTINGS.add(USER_ROTATION); + PUBLIC_SETTINGS.add(DTMF_TONE_WHEN_DIALING); + PUBLIC_SETTINGS.add(SOUND_EFFECTS_ENABLED); + PUBLIC_SETTINGS.add(HAPTIC_FEEDBACK_ENABLED); + PUBLIC_SETTINGS.add(SHOW_WEB_SUGGESTIONS); + } + + /** + * These are all hidden system settings. + * + * @hide + */ + public static final Set<String> PRIVATE_SETTINGS = new ArraySet<>(); + static { + PRIVATE_SETTINGS.add(WIFI_USE_STATIC_IP); + PRIVATE_SETTINGS.add(END_BUTTON_BEHAVIOR); + PRIVATE_SETTINGS.add(ADVANCED_SETTINGS); + PRIVATE_SETTINGS.add(SCREEN_AUTO_BRIGHTNESS_ADJ); + PRIVATE_SETTINGS.add(VIBRATE_INPUT_DEVICES); + PRIVATE_SETTINGS.add(VOLUME_MASTER); + PRIVATE_SETTINGS.add(VOLUME_MASTER_MUTE); + PRIVATE_SETTINGS.add(MICROPHONE_MUTE); + PRIVATE_SETTINGS.add(NOTIFICATIONS_USE_RING_VOLUME); + PRIVATE_SETTINGS.add(VIBRATE_IN_SILENT); + PRIVATE_SETTINGS.add(MEDIA_BUTTON_RECEIVER); + PRIVATE_SETTINGS.add(HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY); + PRIVATE_SETTINGS.add(VIBRATE_WHEN_RINGING); + PRIVATE_SETTINGS.add(DTMF_TONE_TYPE_WHEN_DIALING); + PRIVATE_SETTINGS.add(HEARING_AID); + PRIVATE_SETTINGS.add(TTY_MODE); + PRIVATE_SETTINGS.add(NOTIFICATION_LIGHT_PULSE); + PRIVATE_SETTINGS.add(POINTER_LOCATION); + PRIVATE_SETTINGS.add(SHOW_TOUCHES); + PRIVATE_SETTINGS.add(WINDOW_ORIENTATION_LISTENER_LOG); + PRIVATE_SETTINGS.add(POWER_SOUNDS_ENABLED); + PRIVATE_SETTINGS.add(DOCK_SOUNDS_ENABLED); + PRIVATE_SETTINGS.add(LOCKSCREEN_SOUNDS_ENABLED); + PRIVATE_SETTINGS.add(LOCKSCREEN_DISABLED); + PRIVATE_SETTINGS.add(LOW_BATTERY_SOUND); + PRIVATE_SETTINGS.add(DESK_DOCK_SOUND); + PRIVATE_SETTINGS.add(DESK_UNDOCK_SOUND); + PRIVATE_SETTINGS.add(CAR_DOCK_SOUND); + PRIVATE_SETTINGS.add(CAR_UNDOCK_SOUND); + PRIVATE_SETTINGS.add(LOCK_SOUND); + PRIVATE_SETTINGS.add(UNLOCK_SOUND); + PRIVATE_SETTINGS.add(SIP_RECEIVE_CALLS); + PRIVATE_SETTINGS.add(SIP_CALL_OPTIONS); + PRIVATE_SETTINGS.add(SIP_ALWAYS); + PRIVATE_SETTINGS.add(SIP_ADDRESS_ONLY); + PRIVATE_SETTINGS.add(SIP_ASK_ME_EACH_TIME); + PRIVATE_SETTINGS.add(POINTER_SPEED); + PRIVATE_SETTINGS.add(LOCK_TO_APP_ENABLED); + PRIVATE_SETTINGS.add(EGG_MODE); + } + + /** + * These are all pulbic system settings + * + * @hide + */ + public static final Map<String, Validator> VALIDATORS = new ArrayMap<>(); + static { + VALIDATORS.put(END_BUTTON_BEHAVIOR,END_BUTTON_BEHAVIOR_VALIDATOR); + VALIDATORS.put(WIFI_USE_STATIC_IP, WIFI_USE_STATIC_IP_VALIDATOR); + VALIDATORS.put(BLUETOOTH_DISCOVERABILITY, BLUETOOTH_DISCOVERABILITY_VALIDATOR); + VALIDATORS.put(BLUETOOTH_DISCOVERABILITY_TIMEOUT, + BLUETOOTH_DISCOVERABILITY_TIMEOUT_VALIDATOR); + VALIDATORS.put(NEXT_ALARM_FORMATTED, NEXT_ALARM_FORMATTED_VALIDATOR); + VALIDATORS.put(FONT_SCALE, FONT_SCALE_VALIDATOR); + VALIDATORS.put(DIM_SCREEN, DIM_SCREEN_VALIDATOR); + VALIDATORS.put(SCREEN_OFF_TIMEOUT, SCREEN_OFF_TIMEOUT_VALIDATOR); + VALIDATORS.put(SCREEN_BRIGHTNESS, SCREEN_BRIGHTNESS_VALIDATOR); + VALIDATORS.put(SCREEN_BRIGHTNESS_MODE, SCREEN_BRIGHTNESS_MODE_VALIDATOR); + VALIDATORS.put(MODE_RINGER_STREAMS_AFFECTED, MODE_RINGER_STREAMS_AFFECTED_VALIDATOR); + VALIDATORS.put(MUTE_STREAMS_AFFECTED, MUTE_STREAMS_AFFECTED_VALIDATOR); + VALIDATORS.put(VIBRATE_ON, VIBRATE_ON_VALIDATOR); + VALIDATORS.put(VOLUME_RING, VOLUME_RING_VALIDATOR); + VALIDATORS.put(VOLUME_SYSTEM, VOLUME_SYSTEM_VALIDATOR); + VALIDATORS.put(VOLUME_VOICE, VOLUME_VOICE_VALIDATOR); + VALIDATORS.put(VOLUME_MUSIC, VOLUME_MUSIC_VALIDATOR); + VALIDATORS.put(VOLUME_ALARM, VOLUME_ALARM_VALIDATOR); + VALIDATORS.put(VOLUME_NOTIFICATION, VOLUME_NOTIFICATION_VALIDATOR); + VALIDATORS.put(VOLUME_BLUETOOTH_SCO, VOLUME_BLUETOOTH_SCO_VALIDATOR); + VALIDATORS.put(RINGTONE, RINGTONE_VALIDATOR); + VALIDATORS.put(NOTIFICATION_SOUND, NOTIFICATION_SOUND_VALIDATOR); + VALIDATORS.put(ALARM_ALERT, ALARM_ALERT_VALIDATOR); + VALIDATORS.put(TEXT_AUTO_REPLACE, TEXT_AUTO_REPLACE_VALIDATOR); + VALIDATORS.put(TEXT_AUTO_CAPS, TEXT_AUTO_CAPS_VALIDATOR); + VALIDATORS.put(TEXT_AUTO_PUNCTUATE, TEXT_AUTO_PUNCTUATE_VALIDATOR); + VALIDATORS.put(TEXT_SHOW_PASSWORD, TEXT_SHOW_PASSWORD_VALIDATOR); + VALIDATORS.put(SHOW_GTALK_SERVICE_STATUS, SHOW_GTALK_SERVICE_STATUS_VALIDATOR); + VALIDATORS.put(WALLPAPER_ACTIVITY, WALLPAPER_ACTIVITY_VALIDATOR); + VALIDATORS.put(TIME_12_24, TIME_12_24_VALIDATOR); + VALIDATORS.put(DATE_FORMAT, DATE_FORMAT_VALIDATOR); + VALIDATORS.put(SETUP_WIZARD_HAS_RUN, SETUP_WIZARD_HAS_RUN_VALIDATOR); + VALIDATORS.put(ACCELEROMETER_ROTATION, ACCELEROMETER_ROTATION_VALIDATOR); + VALIDATORS.put(USER_ROTATION, USER_ROTATION_VALIDATOR); + VALIDATORS.put(DTMF_TONE_WHEN_DIALING, DTMF_TONE_WHEN_DIALING_VALIDATOR); + VALIDATORS.put(SOUND_EFFECTS_ENABLED, SOUND_EFFECTS_ENABLED_VALIDATOR); + VALIDATORS.put(HAPTIC_FEEDBACK_ENABLED, HAPTIC_FEEDBACK_ENABLED_VALIDATOR); + VALIDATORS.put(SHOW_WEB_SUGGESTIONS, SHOW_WEB_SUGGESTIONS_VALIDATOR); + VALIDATORS.put(WIFI_USE_STATIC_IP, WIFI_USE_STATIC_IP_VALIDATOR); + VALIDATORS.put(END_BUTTON_BEHAVIOR, END_BUTTON_BEHAVIOR_VALIDATOR); + VALIDATORS.put(ADVANCED_SETTINGS, ADVANCED_SETTINGS_VALIDATOR); + VALIDATORS.put(SCREEN_AUTO_BRIGHTNESS_ADJ, SCREEN_AUTO_BRIGHTNESS_ADJ_VALIDATOR); + VALIDATORS.put(VIBRATE_INPUT_DEVICES, VIBRATE_INPUT_DEVICES_VALIDATOR); + VALIDATORS.put(VOLUME_MASTER, VOLUME_MASTER_VALIDATOR); + VALIDATORS.put(VOLUME_MASTER_MUTE, VOLUME_MASTER_MUTE_VALIDATOR); + VALIDATORS.put(MICROPHONE_MUTE, MICROPHONE_MUTE_VALIDATOR); + VALIDATORS.put(NOTIFICATIONS_USE_RING_VOLUME, NOTIFICATIONS_USE_RING_VOLUME_VALIDATOR); + VALIDATORS.put(VIBRATE_IN_SILENT, VIBRATE_IN_SILENT_VALIDATOR); + VALIDATORS.put(MEDIA_BUTTON_RECEIVER, MEDIA_BUTTON_RECEIVER_VALIDATOR); + VALIDATORS.put(HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, + HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY_VALIDATOR); + VALIDATORS.put(VIBRATE_WHEN_RINGING, VIBRATE_WHEN_RINGING_VALIDATOR); + VALIDATORS.put(DTMF_TONE_TYPE_WHEN_DIALING, DTMF_TONE_TYPE_WHEN_DIALING_VALIDATOR); + VALIDATORS.put(HEARING_AID, HEARING_AID_VALIDATOR); + VALIDATORS.put(TTY_MODE, TTY_MODE_VALIDATOR); + VALIDATORS.put(NOTIFICATION_LIGHT_PULSE, NOTIFICATION_LIGHT_PULSE_VALIDATOR); + VALIDATORS.put(POINTER_LOCATION, POINTER_LOCATION_VALIDATOR); + VALIDATORS.put(SHOW_TOUCHES, SHOW_TOUCHES_VALIDATOR); + VALIDATORS.put(WINDOW_ORIENTATION_LISTENER_LOG, + WINDOW_ORIENTATION_LISTENER_LOG_VALIDATOR); + VALIDATORS.put(LOCKSCREEN_SOUNDS_ENABLED, LOCKSCREEN_SOUNDS_ENABLED_VALIDATOR); + VALIDATORS.put(LOCKSCREEN_DISABLED, LOCKSCREEN_DISABLED_VALIDATOR); + VALIDATORS.put(SIP_RECEIVE_CALLS, SIP_RECEIVE_CALLS_VALIDATOR); + VALIDATORS.put(SIP_CALL_OPTIONS, SIP_CALL_OPTIONS_VALIDATOR); + VALIDATORS.put(SIP_ALWAYS, SIP_ALWAYS_VALIDATOR); + VALIDATORS.put(SIP_ADDRESS_ONLY, SIP_ADDRESS_ONLY_VALIDATOR); + VALIDATORS.put(SIP_ASK_ME_EACH_TIME, SIP_ASK_ME_EACH_TIME_VALIDATOR); + VALIDATORS.put(POINTER_SPEED, POINTER_SPEED_VALIDATOR); + VALIDATORS.put(LOCK_TO_APP_ENABLED, LOCK_TO_APP_ENABLED_VALIDATOR); + VALIDATORS.put(EGG_MODE, EGG_MODE_VALIDATOR); + VALIDATORS.put(WIFI_STATIC_IP, WIFI_STATIC_IP_VALIDATOR); + VALIDATORS.put(WIFI_STATIC_GATEWAY, WIFI_STATIC_GATEWAY_VALIDATOR); + VALIDATORS.put(WIFI_STATIC_NETMASK, WIFI_STATIC_NETMASK_VALIDATOR); + VALIDATORS.put(WIFI_STATIC_DNS1, WIFI_STATIC_DNS1_VALIDATOR); + VALIDATORS.put(WIFI_STATIC_DNS2, WIFI_STATIC_DNS2_VALIDATOR); + } + + /** * These entries are considered common between the personal and the managed profile, * since the managed profile doesn't get to change them. - * @hide */ - public static final String[] CLONE_TO_MANAGED_PROFILE = { - DATE_FORMAT, - HAPTIC_FEEDBACK_ENABLED, - SOUND_EFFECTS_ENABLED, - TEXT_SHOW_PASSWORD, - TIME_12_24 - }; + private static final Set<String> CLONE_TO_MANAGED_PROFILE = new ArraySet<>(); + static { + CLONE_TO_MANAGED_PROFILE.add(DATE_FORMAT); + CLONE_TO_MANAGED_PROFILE.add(HAPTIC_FEEDBACK_ENABLED); + CLONE_TO_MANAGED_PROFILE.add(SOUND_EFFECTS_ENABLED); + CLONE_TO_MANAGED_PROFILE.add(TEXT_SHOW_PASSWORD); + CLONE_TO_MANAGED_PROFILE.add(TIME_12_24); + } + + /** @hide */ + public static void getCloneToManagedProfileSettings(Set<String> outKeySet) { + outKeySet.addAll(CLONE_TO_MANAGED_PROFILE); + } /** * When to use Wi-Fi calling @@ -3099,7 +3627,7 @@ public final class Settings { } /** @hide */ - public static void getMovedKeys(HashSet<String> outKeySet) { + public static void getMovedToGlobalSettings(Set<String> outKeySet) { outKeySet.addAll(MOVED_TO_GLOBAL); } @@ -4762,6 +5290,10 @@ public final class Settings { public static final String BAR_SERVICE_COMPONENT = "bar_service_component"; /** @hide */ + public static final String VOLUME_CONTROLLER_SERVICE_COMPONENT + = "volume_controller_service_component"; + + /** @hide */ public static final String IMMERSIVE_MODE_CONFIRMATIONS = "immersive_mode_confirmations"; /** @@ -4896,22 +5428,27 @@ public final class Settings { /** * These entries are considered common between the personal and the managed profile, * since the managed profile doesn't get to change them. - * @hide */ - public static final String[] CLONE_TO_MANAGED_PROFILE = { - ACCESSIBILITY_ENABLED, - ALLOW_MOCK_LOCATION, - ALLOWED_GEOLOCATION_ORIGINS, - DEFAULT_INPUT_METHOD, - ENABLED_ACCESSIBILITY_SERVICES, - ENABLED_INPUT_METHODS, - LOCATION_MODE, - LOCATION_PROVIDERS_ALLOWED, - LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, - SELECTED_INPUT_METHOD_SUBTYPE, - SELECTED_SPELL_CHECKER, - SELECTED_SPELL_CHECKER_SUBTYPE - }; + private static final Set<String> CLONE_TO_MANAGED_PROFILE = new ArraySet<>(); + static { + CLONE_TO_MANAGED_PROFILE.add(ACCESSIBILITY_ENABLED); + CLONE_TO_MANAGED_PROFILE.add(ALLOW_MOCK_LOCATION); + CLONE_TO_MANAGED_PROFILE.add(ALLOWED_GEOLOCATION_ORIGINS); + CLONE_TO_MANAGED_PROFILE.add(DEFAULT_INPUT_METHOD); + CLONE_TO_MANAGED_PROFILE.add(ENABLED_ACCESSIBILITY_SERVICES); + CLONE_TO_MANAGED_PROFILE.add(ENABLED_INPUT_METHODS); + CLONE_TO_MANAGED_PROFILE.add(LOCATION_MODE); + CLONE_TO_MANAGED_PROFILE.add(LOCATION_PROVIDERS_ALLOWED); + CLONE_TO_MANAGED_PROFILE.add(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); + CLONE_TO_MANAGED_PROFILE.add(SELECTED_INPUT_METHOD_SUBTYPE); + CLONE_TO_MANAGED_PROFILE.add(SELECTED_SPELL_CHECKER); + CLONE_TO_MANAGED_PROFILE.add(SELECTED_SPELL_CHECKER_SUBTYPE); + } + + /** @hide */ + public static void getCloneToManagedProfileSettings(Set<String> outKeySet) { + outKeySet.addAll(CLONE_TO_MANAGED_PROFILE); + } /** * Helper method for determining if a location provider is enabled. @@ -6693,6 +7230,11 @@ public final class Settings { MOVED_TO_SECURE.add(Settings.Global.INSTALL_NON_MARKET_APPS); } + /** @hide */ + public static void getMovedToSecureSettings(Set<String> outKeySet) { + outKeySet.addAll(MOVED_TO_SECURE); + } + /** * Look up a name in the database. * @param resolver to access the database with diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 3c57dda..fa5ac42 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -18,6 +18,7 @@ package android.service.dreams; import java.io.FileDescriptor; import java.io.PrintWriter; +import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.app.AlarmManager; @@ -442,6 +443,7 @@ public class DreamService extends Service implements Window.Callback { * * @return The view if found or null otherwise. */ + @Nullable public View findViewById(int id) { return getWindow().findViewById(id); } diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 3d39b18..0860153 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -77,6 +77,14 @@ public abstract class NotificationListenerService extends Service { */ public static final int INTERRUPTION_FILTER_NONE = 3; + /** {@link #getCurrentInterruptionFilter() Interruption filter} constant - returned when + * the value is unavailable for any reason. For example, before the notification listener + * is connected. + * + * {@see #onListenerConnected()} + */ + public static final int INTERRUPTION_FILTER_UNKNOWN = 0; + /** {@link #getCurrentListenerHints() Listener hints} constant - the primary device UI * should disable notification sound, vibrating and other visual or aural effects. * This does not change the interruption filter, only the effects. **/ @@ -473,15 +481,16 @@ public abstract class NotificationListenerService extends Service { * <p> * Listen for updates using {@link #onInterruptionFilterChanged(int)}. * - * @return One of the INTERRUPTION_FILTER_ constants, or 0 on errors. + * @return One of the INTERRUPTION_FILTER_ constants, or INTERRUPTION_FILTER_UNKNOWN when + * unavailable. */ public final int getCurrentInterruptionFilter() { - if (!isBound()) return 0; + if (!isBound()) return INTERRUPTION_FILTER_UNKNOWN; try { return getNotificationInterface().getInterruptionFilterFromListener(mWrapper); } catch (android.os.RemoteException ex) { Log.v(TAG, "Unable to contact notification manager", ex); - return 0; + return INTERRUPTION_FILTER_UNKNOWN; } } diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java index 06df683..7ce44e1 100644 --- a/core/java/android/text/SpannableStringBuilder.java +++ b/core/java/android/text/SpannableStringBuilder.java @@ -26,6 +26,7 @@ import com.android.internal.util.GrowingArrayUtils; import libcore.util.EmptyArray; import java.lang.reflect.Array; +import java.util.IdentityHashMap; /** * This is the class for text whose content and markup can both be changed. @@ -68,6 +69,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable mSpanStarts = EmptyArray.INT; mSpanEnds = EmptyArray.INT; mSpanFlags = EmptyArray.INT; + mSpanMax = EmptyArray.INT; if (text instanceof Spanned) { Spanned sp = (Spanned) text; @@ -94,6 +96,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable setSpan(false, spans[i], st, en, fl); } + restoreInvariants(); } } @@ -147,9 +150,12 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable if (mGapLength < 1) new Exception("mGapLength < 1").printStackTrace(); - for (int i = 0; i < mSpanCount; i++) { - if (mSpanStarts[i] > mGapStart) mSpanStarts[i] += delta; - if (mSpanEnds[i] > mGapStart) mSpanEnds[i] += delta; + if (mSpanCount != 0) { + for (int i = 0; i < mSpanCount; i++) { + if (mSpanStarts[i] > mGapStart) mSpanStarts[i] += delta; + if (mSpanEnds[i] > mGapStart) mSpanEnds[i] += delta; + } + calcMax(treeRoot()); } } @@ -167,35 +173,38 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable System.arraycopy(mText, where + mGapLength - overlap, mText, mGapStart, overlap); } - // XXX be more clever - for (int i = 0; i < mSpanCount; i++) { - int start = mSpanStarts[i]; - int end = mSpanEnds[i]; - - if (start > mGapStart) - start -= mGapLength; - if (start > where) - start += mGapLength; - else if (start == where) { - int flag = (mSpanFlags[i] & START_MASK) >> START_SHIFT; + // TODO: be more clever (although the win really isn't that big) + if (mSpanCount != 0) { + for (int i = 0; i < mSpanCount; i++) { + int start = mSpanStarts[i]; + int end = mSpanEnds[i]; - if (flag == POINT || (atEnd && flag == PARAGRAPH)) + if (start > mGapStart) + start -= mGapLength; + if (start > where) start += mGapLength; - } + else if (start == where) { + int flag = (mSpanFlags[i] & START_MASK) >> START_SHIFT; - if (end > mGapStart) - end -= mGapLength; - if (end > where) - end += mGapLength; - else if (end == where) { - int flag = (mSpanFlags[i] & END_MASK); + if (flag == POINT || (atEnd && flag == PARAGRAPH)) + start += mGapLength; + } - if (flag == POINT || (atEnd && flag == PARAGRAPH)) + if (end > mGapStart) + end -= mGapLength; + if (end > where) end += mGapLength; - } + else if (end == where) { + int flag = (mSpanFlags[i] & END_MASK); + + if (flag == POINT || (atEnd && flag == PARAGRAPH)) + end += mGapLength; + } - mSpanStarts[i] = start; - mSpanEnds[i] = end; + mSpanStarts[i] = start; + mSpanEnds[i] = end; + } + calcMax(treeRoot()); } mGapStart = where; @@ -243,6 +252,9 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable sendSpanRemoved(what, ostart, oend); } + if (mIndexOfSpan != null) { + mIndexOfSpan.clear(); + } } // Documentation from interface @@ -277,12 +289,39 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable return append(String.valueOf(text)); } + // Returns true if a node was removed (so we can restart search from root) + private boolean removeSpansForChange(int start, int end, boolean textIsRemoved, int i) { + if ((i & 1) != 0) { + // internal tree node + if (resolveGap(mSpanMax[i]) >= start && + removeSpansForChange(start, end, textIsRemoved, leftChild(i))) { + return true; + } + } + if (i < mSpanCount) { + if ((mSpanFlags[i] & Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) == + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE && + mSpanStarts[i] >= start && mSpanStarts[i] < mGapStart + mGapLength && + mSpanEnds[i] >= start && mSpanEnds[i] < mGapStart + mGapLength && + // The following condition indicates that the span would become empty + (textIsRemoved || mSpanStarts[i] > start || mSpanEnds[i] < mGapStart)) { + mIndexOfSpan.remove(mSpans[i]); + removeSpan(i); + return true; + } + return resolveGap(mSpanStarts[i]) <= end && (i & 1) != 0 && + removeSpansForChange(start, end, textIsRemoved, rightChild(i)); + } + return false; + } + private void change(int start, int end, CharSequence cs, int csStart, int csEnd) { // Can be negative final int replacedLength = end - start; final int replacementLength = csEnd - csStart; final int nbNewChars = replacementLength - replacedLength; + boolean changed = false; for (int i = mSpanCount - 1; i >= 0; i--) { int spanStart = mSpanStarts[i]; if (spanStart > mGapStart) @@ -309,8 +348,10 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable break; } - if (spanStart != ost || spanEnd != oen) + if (spanStart != ost || spanEnd != oen) { setSpan(false, mSpans[i], spanStart, spanEnd, mSpanFlags[i]); + changed = true; + } } int flags = 0; @@ -320,6 +361,9 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable else if (spanEnd == end + nbNewChars) flags |= SPAN_END_AT_END; mSpanFlags[i] |= flags; } + if (changed) { + restoreInvariants(); + } moveGapTo(end); @@ -331,23 +375,10 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable // The removal pass needs to be done before the gap is updated in order to broadcast the // correct previous positions to the correct intersecting SpanWatchers if (replacedLength > 0) { // no need for span fixup on pure insertion - // A for loop will not work because the array is being modified - // Do not iterate in reverse to keep the SpanWatchers notified in ordering - // Also, a removed SpanWatcher should not get notified of removed spans located - // further in the span array. - int i = 0; - while (i < mSpanCount) { - if ((mSpanFlags[i] & Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) == - Spanned.SPAN_EXCLUSIVE_EXCLUSIVE && - mSpanStarts[i] >= start && mSpanStarts[i] < mGapStart + mGapLength && - mSpanEnds[i] >= start && mSpanEnds[i] < mGapStart + mGapLength && - // This condition indicates that the span would become empty - (textIsRemoved || mSpanStarts[i] > start || mSpanEnds[i] < mGapStart)) { - removeSpan(i); - continue; // do not increment i, spans will be shifted left in the array - } - - i++; + while (mSpanCount > 0 && + removeSpansForChange(start, end, textIsRemoved, treeRoot())) { + // keep deleting spans as needed, and restart from root after every deletion + // because deletion can invalidate an index. } } @@ -360,6 +391,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable TextUtils.getChars(cs, csStart, csEnd, mText, start); if (replacedLength > 0) { // no need for span fixup on pure insertion + // TODO potential optimization: only update bounds on intersecting spans final boolean atEnd = (mGapStart + mGapLength == mText.length); for (int i = 0; i < mSpanCount; i++) { @@ -371,10 +403,10 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable mSpanEnds[i] = updatedIntervalBound(mSpanEnds[i], start, nbNewChars, endFlag, atEnd, textIsRemoved); } + // TODO potential optimization: only fix up invariants when bounds actually changed + restoreInvariants(); } - mSpanCountBeforeAdd = mSpanCount; - if (cs instanceof Spanned) { Spanned sp = (Spanned) cs; Object[] spans = sp.getSpans(csStart, csEnd, Object.class); @@ -389,9 +421,10 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable // Add span only if this object is not yet used as a span in this string if (getSpanStart(spans[i]) < 0) { setSpan(false, spans[i], st - csStart + start, en - csStart + start, - sp.getSpanFlags(spans[i])); + sp.getSpanFlags(spans[i]) | SPAN_ADDED); } } + restoreInvariants(); } } @@ -427,6 +460,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable return offset; } + // Note: caller is responsible for removing the mIndexOfSpan entry. private void removeSpan(int i) { Object object = mSpans[i]; @@ -444,8 +478,12 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable mSpanCount--; + invalidateIndex(i); mSpans[mSpanCount] = null; + // Invariants must be restored before sending span removed notifications. + restoreInvariants(); + sendSpanRemoved(object, start, end); } @@ -496,10 +534,12 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable change(start, end, tb, tbstart, tbend); if (adjustSelection) { + boolean changed = false; if (selectionStart > start && selectionStart < end) { final int offset = (selectionStart - start) * newLen / origLen; selectionStart = start + offset; + changed = true; setSpan(false, Selection.SELECTION_START, selectionStart, selectionStart, Spanned.SPAN_POINT_POINT); } @@ -507,9 +547,13 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable final int offset = (selectionEnd - start) * newLen / origLen; selectionEnd = start + offset; + changed = true; setSpan(false, Selection.SELECTION_END, selectionEnd, selectionEnd, Spanned.SPAN_POINT_POINT); } + if (changed) { + restoreInvariants(); + } } sendTextChanged(textWatchers, start, origLen, newLen); @@ -536,12 +580,15 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable } private void sendToSpanWatchers(int replaceStart, int replaceEnd, int nbNewChars) { - for (int i = 0; i < mSpanCountBeforeAdd; i++) { + for (int i = 0; i < mSpanCount; i++) { + int spanFlags = mSpanFlags[i]; + + // This loop handles only modified (not added) spans. + if ((spanFlags & SPAN_ADDED) != 0) continue; int spanStart = mSpanStarts[i]; int spanEnd = mSpanEnds[i]; if (spanStart > mGapStart) spanStart -= mGapLength; if (spanEnd > mGapStart) spanEnd -= mGapLength; - int spanFlags = mSpanFlags[i]; int newReplaceEnd = replaceEnd + nbNewChars; boolean spanChanged = false; @@ -588,13 +635,17 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable mSpanFlags[i] &= ~SPAN_START_END_MASK; } - // The spans starting at mIntermediateSpanCount were added from the replacement text - for (int i = mSpanCountBeforeAdd; i < mSpanCount; i++) { - int spanStart = mSpanStarts[i]; - int spanEnd = mSpanEnds[i]; - if (spanStart > mGapStart) spanStart -= mGapLength; - if (spanEnd > mGapStart) spanEnd -= mGapLength; - sendSpanAdded(mSpans[i], spanStart, spanEnd); + // Handle added spans + for (int i = 0; i < mSpanCount; i++) { + int spanFlags = mSpanFlags[i]; + if ((spanFlags & SPAN_ADDED) != 0) { + mSpanFlags[i] &= ~SPAN_ADDED; + int spanStart = mSpanStarts[i]; + int spanEnd = mSpanEnds[i]; + if (spanStart > mGapStart) spanStart -= mGapLength; + if (spanEnd > mGapStart) spanEnd -= mGapLength; + sendSpanAdded(mSpans[i], spanStart, spanEnd); + } } } @@ -607,6 +658,9 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable setSpan(true, what, start, end, flags); } + // Note: if send is false, then it is the caller's responsibility to restore + // invariants. If send is false and the span already exists, then this method + // will not change the index of any spans. private void setSpan(boolean send, Object what, int start, int end, int flags) { checkRange("setSpan", start, end); @@ -661,8 +715,10 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable int count = mSpanCount; Object[] spans = mSpans; - for (int i = 0; i < count; i++) { - if (spans[i] == what) { + if (mIndexOfSpan != null) { + Integer index = mIndexOfSpan.get(what); + if (index != null) { + int i = index; int ostart = mSpanStarts[i]; int oend = mSpanEnds[i]; @@ -675,7 +731,10 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable mSpanEnds[i] = end; mSpanFlags[i] = flags; - if (send) sendSpanChanged(what, ostart, oend, nstart, nend); + if (send) { + restoreInvariants(); + sendSpanChanged(what, ostart, oend, nstart, nend); + } return; } @@ -685,43 +744,48 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable mSpanStarts = GrowingArrayUtils.append(mSpanStarts, mSpanCount, start); mSpanEnds = GrowingArrayUtils.append(mSpanEnds, mSpanCount, end); mSpanFlags = GrowingArrayUtils.append(mSpanFlags, mSpanCount, flags); + invalidateIndex(mSpanCount); mSpanCount++; + // Make sure there is enough room for empty interior nodes. + // This magic formula computes the size of the smallest perfect binary + // tree no smaller than mSpanCount. + int sizeOfMax = 2 * treeRoot() + 1; + if (mSpanMax.length < sizeOfMax) { + mSpanMax = new int[sizeOfMax]; + } - if (send) sendSpanAdded(what, nstart, nend); + if (send) { + restoreInvariants(); + sendSpanAdded(what, nstart, nend); + } } /** * Remove the specified markup object from the buffer. */ public void removeSpan(Object what) { - for (int i = mSpanCount - 1; i >= 0; i--) { - if (mSpans[i] == what) { - removeSpan(i); - return; - } + if (mIndexOfSpan == null) return; + Integer i = mIndexOfSpan.remove(what); + if (i != null) { + removeSpan(i.intValue()); } } /** + * Return externally visible offset given offset into gapped buffer. + */ + private int resolveGap(int i) { + return i > mGapStart ? i - mGapLength : i; + } + + /** * Return the buffer offset of the beginning of the specified * markup object, or -1 if it is not attached to this buffer. */ public int getSpanStart(Object what) { - int count = mSpanCount; - Object[] spans = mSpans; - - for (int i = count - 1; i >= 0; i--) { - if (spans[i] == what) { - int where = mSpanStarts[i]; - - if (where > mGapStart) - where -= mGapLength; - - return where; - } - } - - return -1; + if (mIndexOfSpan == null) return -1; + Integer i = mIndexOfSpan.get(what); + return i == null ? -1 : resolveGap(mSpanStarts[i]); } /** @@ -729,21 +793,9 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable * markup object, or -1 if it is not attached to this buffer. */ public int getSpanEnd(Object what) { - int count = mSpanCount; - Object[] spans = mSpans; - - for (int i = count - 1; i >= 0; i--) { - if (spans[i] == what) { - int where = mSpanEnds[i]; - - if (where > mGapStart) - where -= mGapLength; - - return where; - } - } - - return -1; + if (mIndexOfSpan == null) return -1; + Integer i = mIndexOfSpan.get(what); + return i == null ? -1 : resolveGap(mSpanEnds[i]); } /** @@ -751,16 +803,9 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable * markup object, or 0 if it is not attached to this buffer. */ public int getSpanFlags(Object what) { - int count = mSpanCount; - Object[] spans = mSpans; - - for (int i = count - 1; i >= 0; i--) { - if (spans[i] == what) { - return mSpanFlags[i]; - } - } - - return 0; + if (mIndexOfSpan == null) return 0; + Integer i = mIndexOfSpan.get(what); + return i == null ? 0 : mSpanFlags[i]; } /** @@ -770,59 +815,84 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable */ @SuppressWarnings("unchecked") public <T> T[] getSpans(int queryStart, int queryEnd, Class<T> kind) { - if (kind == null) return ArrayUtils.emptyArray(kind); + if (kind == null || mSpanCount == 0) return ArrayUtils.emptyArray(kind); + int count = countSpans(queryStart, queryEnd, kind, treeRoot()); + if (count == 0) { + return ArrayUtils.emptyArray(kind); + } - int spanCount = mSpanCount; - Object[] spans = mSpans; - int[] starts = mSpanStarts; - int[] ends = mSpanEnds; - int[] flags = mSpanFlags; - int gapstart = mGapStart; - int gaplen = mGapLength; + // Safe conversion, but requires a suppressWarning + T[] ret = (T[]) Array.newInstance(kind, count); + getSpansRec(queryStart, queryEnd, kind, treeRoot(), ret, 0); + return ret; + } + private int countSpans(int queryStart, int queryEnd, Class kind, int i) { int count = 0; - T[] ret = null; - T ret1 = null; - - for (int i = 0; i < spanCount; i++) { - int spanStart = starts[i]; - if (spanStart > gapstart) { - spanStart -= gaplen; + if ((i & 1) != 0) { + // internal tree node + int left = leftChild(i); + int spanMax = mSpanMax[left]; + if (spanMax > mGapStart) { + spanMax -= mGapLength; } - if (spanStart > queryEnd) { - continue; + if (spanMax >= queryStart) { + count = countSpans(queryStart, queryEnd, kind, left); } - - int spanEnd = ends[i]; - if (spanEnd > gapstart) { - spanEnd -= gaplen; + } + if (i < mSpanCount) { + int spanStart = mSpanStarts[i]; + if (spanStart > mGapStart) { + spanStart -= mGapLength; } - if (spanEnd < queryStart) { - continue; + if (spanStart <= queryEnd) { + int spanEnd = mSpanEnds[i]; + if (spanEnd > mGapStart) { + spanEnd -= mGapLength; + } + if (spanEnd >= queryStart && + (spanStart == spanEnd || queryStart == queryEnd || + (spanStart != queryEnd && spanEnd != queryStart)) && + kind.isInstance(mSpans[i])) { + count++; + } + if ((i & 1) != 0) { + count += countSpans(queryStart, queryEnd, kind, rightChild(i)); + } } + } + return count; + } - if (spanStart != spanEnd && queryStart != queryEnd) { - if (spanStart == queryEnd) - continue; - if (spanEnd == queryStart) - continue; + @SuppressWarnings("unchecked") + private <T> int getSpansRec(int queryStart, int queryEnd, Class<T> kind, + int i, T[] ret, int count) { + if ((i & 1) != 0) { + // internal tree node + int left = leftChild(i); + int spanMax = mSpanMax[left]; + if (spanMax > mGapStart) { + spanMax -= mGapLength; } - - // Expensive test, should be performed after the previous tests - if (!kind.isInstance(spans[i])) continue; - - if (count == 0) { - // Safe conversion thanks to the isInstance test above - ret1 = (T) spans[i]; - count++; - } else { - if (count == 1) { - // Safe conversion, but requires a suppressWarning - ret = (T[]) Array.newInstance(kind, spanCount - i + 1); - ret[0] = ret1; - } - - int prio = flags[i] & SPAN_PRIORITY; + if (spanMax >= queryStart) { + count = getSpansRec(queryStart, queryEnd, kind, left, ret, count); + } + } + if (i >= mSpanCount) return count; + int spanStart = mSpanStarts[i]; + if (spanStart > mGapStart) { + spanStart -= mGapLength; + } + if (spanStart <= queryEnd) { + int spanEnd = mSpanEnds[i]; + if (spanEnd > mGapStart) { + spanEnd -= mGapLength; + } + if (spanEnd >= queryStart && + (spanStart == spanEnd || queryStart == queryEnd || + (spanStart != queryEnd && spanEnd != queryStart)) && + kind.isInstance(mSpans[i])) { + int prio = mSpanFlags[i] & SPAN_PRIORITY; if (prio != 0) { int j; @@ -836,32 +906,18 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable System.arraycopy(ret, j, ret, j + 1, count - j); // Safe conversion thanks to the isInstance test above - ret[j] = (T) spans[i]; - count++; + ret[j] = (T) mSpans[i]; } else { // Safe conversion thanks to the isInstance test above - ret[count++] = (T) spans[i]; + ret[count] = (T) mSpans[i]; } + count++; + } + if (count < ret.length && (i & 1) != 0) { + count = getSpansRec(queryStart, queryEnd, kind, rightChild(i), ret, count); } } - - if (count == 0) { - return ArrayUtils.emptyArray(kind); - } - if (count == 1) { - // Safe conversion, but requires a suppressWarning - ret = (T[]) Array.newInstance(kind, 1); - ret[0] = ret1; - return ret; - } - if (count == ret.length) { - return ret; - } - - // Safe conversion, but requires a suppressWarning - T[] nret = (T[]) Array.newInstance(kind, count); - System.arraycopy(ret, 0, nret, 0, count); - return nret; + return count; } /** @@ -870,30 +926,31 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable * begins or ends. */ public int nextSpanTransition(int start, int limit, Class kind) { - int count = mSpanCount; - Object[] spans = mSpans; - int[] starts = mSpanStarts; - int[] ends = mSpanEnds; - int gapstart = mGapStart; - int gaplen = mGapLength; - + if (mSpanCount == 0) return limit; if (kind == null) { kind = Object.class; } + return nextSpanTransitionRec(start, limit, kind, treeRoot()); + } - for (int i = 0; i < count; i++) { - int st = starts[i]; - int en = ends[i]; - - if (st > gapstart) - st -= gaplen; - if (en > gapstart) - en -= gaplen; - - if (st > start && st < limit && kind.isInstance(spans[i])) + private int nextSpanTransitionRec(int start, int limit, Class kind, int i) { + if ((i & 1) != 0) { + // internal tree node + int left = leftChild(i); + if (resolveGap(mSpanMax[left]) > start) { + limit = nextSpanTransitionRec(start, limit, kind, left); + } + } + if (i < mSpanCount) { + int st = resolveGap(mSpanStarts[i]); + int en = resolveGap(mSpanEnds[i]); + if (st > start && st < limit && kind.isInstance(mSpans[i])) limit = st; - if (en > start && en < limit && kind.isInstance(spans[i])) + if (en > start && en < limit && kind.isInstance(mSpans[i])) limit = en; + if (st < limit && (i & 1) != 0) { + limit = nextSpanTransitionRec(start, limit, kind, rightChild(i)); + } } return limit; @@ -1339,6 +1396,118 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable return hash; } + // Primitives for treating span list as binary tree + + // The spans (along with start and end offsets and flags) are stored in linear arrays sorted + // by start offset. For fast searching, there is a binary search structure imposed over these + // arrays. This structure is inorder traversal of a perfect binary tree, a slightly unusual + // but advantageous approach. + + // The value-containing nodes are indexed 0 <= i < n (where n = mSpanCount), thus preserving + // logic that accesses the values as a contiguous array. Other balanced binary tree approaches + // (such as a complete binary tree) would require some shuffling of node indices. + + // Basic properties of this structure: For a perfect binary tree of height m: + // The tree has 2^(m+1) - 1 total nodes. + // The root of the tree has index 2^m - 1. + // All leaf nodes have even index, all interior nodes odd. + // The height of a node of index i is the number of trailing ones in i's binary representation. + // The left child of a node i of height h is i - 2^(h - 1). + // The right child of a node i of height h is i + 2^(h - 1). + + // Note that for arbitrary n, interior nodes of this tree may be >= n. Thus, the general + // structure of a recursive traversal of node i is: + // * traverse left child if i is an interior node + // * process i if i < n + // * traverse right child if i is an interior node and i < n + + private int treeRoot() { + return Integer.highestOneBit(mSpanCount) - 1; + } + + // (i+1) & ~i is equal to 2^(the number of trailing ones in i) + private static int leftChild(int i) { + return i - (((i + 1) & ~i) >> 1); + } + + private static int rightChild(int i) { + return i + (((i + 1) & ~i) >> 1); + } + + // The span arrays are also augmented by an mSpanMax[] array that represents an interval tree + // over the binary tree structure described above. For each node, the mSpanMax[] array contains + // the maximum value of mSpanEnds of that node and its descendants. Thus, traversals can + // easily reject subtrees that contain no spans overlapping the area of interest. + + // Note that mSpanMax[] also has a valid valuefor interior nodes of index >= n, but which have + // descendants of index < n. In these cases, it simply represents the maximum span end of its + // descendants. This is a consequence of the perfect binary tree structure. + private int calcMax(int i) { + int max = 0; + if ((i & 1) != 0) { + // internal tree node + max = calcMax(leftChild(i)); + } + if (i < mSpanCount) { + max = Math.max(max, mSpanEnds[i]); + if ((i & 1) != 0) { + max = Math.max(max, calcMax(rightChild(i))); + } + } + mSpanMax[i] = max; + return max; + } + + // restores binary interval tree invariants after any mutation of span structure + private void restoreInvariants() { + if (mSpanCount == 0) return; + + // invariant 1: span starts are nondecreasing + + // This is a simple insertion sort because we expect it to be mostly sorted. + for (int i = 1; i < mSpanCount; i++) { + if (mSpanStarts[i] < mSpanStarts[i - 1]) { + Object span = mSpans[i]; + int start = mSpanStarts[i]; + int end = mSpanEnds[i]; + int flags = mSpanFlags[i]; + int j = i; + do { + mSpans[j] = mSpans[j - 1]; + mSpanStarts[j] = mSpanStarts[j - 1]; + mSpanEnds[j] = mSpanEnds[j - 1]; + mSpanFlags[j] = mSpanFlags[j - 1]; + j--; + } while (j > 0 && start < mSpanStarts[j - 1]); + mSpans[j] = span; + mSpanStarts[j] = start; + mSpanEnds[j] = end; + mSpanFlags[j] = flags; + invalidateIndex(j); + } + } + + // invariant 2: max is max span end for each node and its descendants + calcMax(treeRoot()); + + // invariant 3: mIndexOfSpan maps spans back to indices + if (mIndexOfSpan == null) { + mIndexOfSpan = new IdentityHashMap<Object, Integer>(); + } + for (int i = mLowWaterMark; i < mSpanCount; i++) { + Integer existing = mIndexOfSpan.get(mSpans[i]); + if (existing == null || existing != i) { + mIndexOfSpan.put(mSpans[i], i); + } + } + mLowWaterMark = Integer.MAX_VALUE; + } + + // Call this on any update to mSpans[], so that mIndexOfSpan can be updated + private void invalidateIndex(int i) { + mLowWaterMark = Math.min(i, mLowWaterMark); + } + private static final InputFilter[] NO_FILTERS = new InputFilter[0]; private InputFilter[] mFilters = NO_FILTERS; @@ -1349,9 +1518,11 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable private Object[] mSpans; private int[] mSpanStarts; private int[] mSpanEnds; + private int[] mSpanMax; // see calcMax() for an explanation of what this array stores private int[] mSpanFlags; private int mSpanCount; - private int mSpanCountBeforeAdd; + private IdentityHashMap<Object, Integer> mIndexOfSpan; + private int mLowWaterMark; // indices below this have not been touched // TODO These value are tightly related to the public SPAN_MARK/POINT values in {@link Spanned} private static final int MARK = 1; @@ -1363,6 +1534,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable private static final int START_SHIFT = 4; // These bits are not (currently) used by SPANNED flags + private static final int SPAN_ADDED = 0x800; private static final int SPAN_START_AT_START = 0x1000; private static final int SPAN_START_AT_END = 0x2000; private static final int SPAN_END_AT_START = 0x4000; diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java index d0ed871..ac98e8a 100644 --- a/core/java/android/text/format/DateUtils.java +++ b/core/java/android/text/format/DateUtils.java @@ -28,9 +28,11 @@ import java.util.Date; import java.util.Formatter; import java.util.GregorianCalendar; import java.util.Locale; +import java.util.TimeZone; import libcore.icu.DateIntervalFormat; import libcore.icu.LocaleData; +import libcore.icu.RelativeDateTimeFormatter; /** * This class contains various date-related utilities for creating text for things like @@ -242,6 +244,8 @@ public class DateUtils /** * Returns a string describing the elapsed time since startTime. + * <p> + * The minimum timespan to report is set to {@link #MINUTE_IN_MILLIS}. * @param startTime some time in the past. * @return a String object containing the elapsed time. * @see #getRelativeTimeSpanString(long, long, long) @@ -289,69 +293,8 @@ public class DateUtils */ public static CharSequence getRelativeTimeSpanString(long time, long now, long minResolution, int flags) { - Resources r = Resources.getSystem(); - boolean abbrevRelative = (flags & (FORMAT_ABBREV_RELATIVE | FORMAT_ABBREV_ALL)) != 0; - - boolean past = (now >= time); - long duration = Math.abs(now - time); - - int resId; - long count; - if (duration < MINUTE_IN_MILLIS && minResolution < MINUTE_IN_MILLIS) { - count = duration / SECOND_IN_MILLIS; - if (past) { - if (abbrevRelative) { - resId = com.android.internal.R.plurals.abbrev_num_seconds_ago; - } else { - resId = com.android.internal.R.plurals.num_seconds_ago; - } - } else { - if (abbrevRelative) { - resId = com.android.internal.R.plurals.abbrev_in_num_seconds; - } else { - resId = com.android.internal.R.plurals.in_num_seconds; - } - } - } else if (duration < HOUR_IN_MILLIS && minResolution < HOUR_IN_MILLIS) { - count = duration / MINUTE_IN_MILLIS; - if (past) { - if (abbrevRelative) { - resId = com.android.internal.R.plurals.abbrev_num_minutes_ago; - } else { - resId = com.android.internal.R.plurals.num_minutes_ago; - } - } else { - if (abbrevRelative) { - resId = com.android.internal.R.plurals.abbrev_in_num_minutes; - } else { - resId = com.android.internal.R.plurals.in_num_minutes; - } - } - } else if (duration < DAY_IN_MILLIS && minResolution < DAY_IN_MILLIS) { - count = duration / HOUR_IN_MILLIS; - if (past) { - if (abbrevRelative) { - resId = com.android.internal.R.plurals.abbrev_num_hours_ago; - } else { - resId = com.android.internal.R.plurals.num_hours_ago; - } - } else { - if (abbrevRelative) { - resId = com.android.internal.R.plurals.abbrev_in_num_hours; - } else { - resId = com.android.internal.R.plurals.in_num_hours; - } - } - } else if (duration < WEEK_IN_MILLIS && minResolution < WEEK_IN_MILLIS) { - return getRelativeDayString(r, time, now); - } else { - // We know that we won't be showing the time, so it is safe to pass - // in a null context. - return formatDateRange(null, time, time, flags); - } - - String format = r.getQuantityString(resId, (int) count); - return String.format(format, count); + return RelativeDateTimeFormatter.getRelativeTimeSpanString(Locale.getDefault(), + TimeZone.getDefault(), time, now, minResolution, flags); } /** @@ -360,8 +303,8 @@ public class DateUtils * <p> * Example output strings for the US date format. * <ul> - * <li>3 mins ago, 10:15 AM</li> - * <li>yesterday, 12:20 PM</li> + * <li>3 min. ago, 10:15 AM</li> + * <li>Yesterday, 12:20 PM</li> * <li>Dec 12, 4:12 AM</li> * <li>11/14/2007, 8:20 AM</li> * </ul> @@ -374,86 +317,19 @@ public class DateUtils * @param transitionResolution the elapsed time (in milliseconds) at which * to stop reporting relative measurements. Elapsed times greater * than this resolution will default to normal date formatting. - * For example, will transition from "6 days ago" to "Dec 12" + * For example, will transition from "7 days ago" to "Dec 12" * when using {@link #WEEK_IN_MILLIS}. */ public static CharSequence getRelativeDateTimeString(Context c, long time, long minResolution, long transitionResolution, int flags) { - Resources r = Resources.getSystem(); - - long now = System.currentTimeMillis(); - long duration = Math.abs(now - time); - - // getRelativeTimeSpanString() doesn't correctly format relative dates - // above a week or exact dates below a day, so clamp - // transitionResolution as needed. - if (transitionResolution > WEEK_IN_MILLIS) { - transitionResolution = WEEK_IN_MILLIS; - } else if (transitionResolution < DAY_IN_MILLIS) { - transitionResolution = DAY_IN_MILLIS; - } - - CharSequence timeClause = formatDateRange(c, time, time, FORMAT_SHOW_TIME); - - String result; - if (duration < transitionResolution) { - CharSequence relativeClause = getRelativeTimeSpanString(time, now, minResolution, flags); - result = r.getString(com.android.internal.R.string.relative_time, relativeClause, timeClause); - } else { - CharSequence dateClause = getRelativeTimeSpanString(c, time, false); - result = r.getString(com.android.internal.R.string.date_time, dateClause, timeClause); - } - - return result; - } - - /** - * Returns a string describing a day relative to the current day. For example if the day is - * today this function returns "Today", if the day was a week ago it returns "7 days ago", and - * if the day is in 2 weeks it returns "in 14 days". - * - * @param r the resources - * @param day the relative day to describe in UTC milliseconds - * @param today the current time in UTC milliseconds - */ - private static final String getRelativeDayString(Resources r, long day, long today) { - Locale locale = r.getConfiguration().locale; - if (locale == null) { - locale = Locale.getDefault(); - } - - // TODO: use TimeZone.getOffset instead. - Time startTime = new Time(); - startTime.set(day); - int startDay = Time.getJulianDay(day, startTime.gmtoff); - - Time currentTime = new Time(); - currentTime.set(today); - int currentDay = Time.getJulianDay(today, currentTime.gmtoff); - - int days = Math.abs(currentDay - startDay); - boolean past = (today > day); - - // TODO: some locales name other days too, such as de_DE's "Vorgestern" (today - 2). - if (days == 1) { - if (past) { - return LocaleData.get(locale).yesterday; - } else { - return LocaleData.get(locale).tomorrow; - } - } else if (days == 0) { - return LocaleData.get(locale).today; - } - - int resId; - if (past) { - resId = com.android.internal.R.plurals.num_days_ago; - } else { - resId = com.android.internal.R.plurals.in_num_days; + // Same reason as in formatDateRange() to explicitly indicate 12- or 24-hour format. + if ((flags & (FORMAT_SHOW_TIME | FORMAT_12HOUR | FORMAT_24HOUR)) == FORMAT_SHOW_TIME) { + flags |= DateFormat.is24HourFormat(c) ? FORMAT_24HOUR : FORMAT_12HOUR; } - String format = r.getQuantityString(resId, days); - return String.format(format, days); + return RelativeDateTimeFormatter.getRelativeDateTimeString(Locale.getDefault(), + TimeZone.getDefault(), time, System.currentTimeMillis(), minResolution, + transitionResolution, flags); } private static void initFormatStrings() { diff --git a/core/java/android/transition/ArcMotion.java b/core/java/android/transition/ArcMotion.java index f95fb49..70dfe7f 100644 --- a/core/java/android/transition/ArcMotion.java +++ b/core/java/android/transition/ArcMotion.java @@ -21,7 +21,6 @@ import android.content.Context; import android.content.res.TypedArray; import android.graphics.Path; import android.util.AttributeSet; -import android.util.FloatMath; /** * A PathMotion that generates a curved path along an arc on an imaginary circle containing @@ -257,7 +256,7 @@ public class ArcMotion extends PathMotion { } if (newArcDistance2 != 0) { float ratio2 = newArcDistance2 / arcDist2; - float ratio = FloatMath.sqrt(ratio2); + float ratio = (float) Math.sqrt(ratio2); ex = dx + (ratio * (ex - dx)); ey = dy + (ratio * (ey - dy)); } diff --git a/core/java/android/transition/CircularPropagation.java b/core/java/android/transition/CircularPropagation.java index 51beb51..1e44cfa 100644 --- a/core/java/android/transition/CircularPropagation.java +++ b/core/java/android/transition/CircularPropagation.java @@ -16,7 +16,6 @@ package android.transition; import android.graphics.Rect; -import android.util.FloatMath; import android.util.Log; import android.view.View; import android.view.ViewGroup; @@ -87,9 +86,9 @@ public class CircularPropagation extends VisibilityPropagation { epicenterY = Math.round(loc[1] + (sceneRoot.getHeight() / 2) + sceneRoot.getTranslationY()); } - float distance = distance(viewCenterX, viewCenterY, epicenterX, epicenterY); - float maxDistance = distance(0, 0, sceneRoot.getWidth(), sceneRoot.getHeight()); - float distanceFraction = distance/maxDistance; + double distance = distance(viewCenterX, viewCenterY, epicenterX, epicenterY); + double maxDistance = distance(0, 0, sceneRoot.getWidth(), sceneRoot.getHeight()); + double distanceFraction = distance/maxDistance; long duration = transition.getDuration(); if (duration < 0) { @@ -99,9 +98,9 @@ public class CircularPropagation extends VisibilityPropagation { return Math.round(duration * directionMultiplier / mPropagationSpeed * distanceFraction); } - private static float distance(float x1, float y1, float x2, float y2) { - float x = x2 - x1; - float y = y2 - y1; - return FloatMath.sqrt((x * x) + (y * y)); + private static double distance(float x1, float y1, float x2, float y2) { + double x = x2 - x1; + double y = y2 - y1; + return Math.hypot(x, y); } } diff --git a/core/java/android/transition/Explode.java b/core/java/android/transition/Explode.java index 0ccdf15..788676a 100644 --- a/core/java/android/transition/Explode.java +++ b/core/java/android/transition/Explode.java @@ -22,7 +22,6 @@ import android.animation.TimeInterpolator; import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; -import android.util.FloatMath; import android.view.View; import android.view.ViewGroup; import android.view.animation.AccelerateInterpolator; @@ -143,32 +142,29 @@ public class Explode extends Visibility { int centerX = bounds.centerX(); int centerY = bounds.centerY(); - float xVector = centerX - focalX; - float yVector = centerY - focalY; + double xVector = centerX - focalX; + double yVector = centerY - focalY; if (xVector == 0 && yVector == 0) { // Random direction when View is centered on focal View. - xVector = (float) (Math.random() * 2) - 1; - yVector = (float) (Math.random() * 2) - 1; + xVector = (Math.random() * 2) - 1; + yVector = (Math.random() * 2) - 1; } - float vectorSize = calculateDistance(xVector, yVector); + double vectorSize = Math.hypot(xVector, yVector); xVector /= vectorSize; yVector /= vectorSize; - float maxDistance = + double maxDistance = calculateMaxDistance(sceneRoot, focalX - sceneRootX, focalY - sceneRootY); - outVector[0] = Math.round(maxDistance * xVector); - outVector[1] = Math.round(maxDistance * yVector); + outVector[0] = (int) Math.round(maxDistance * xVector); + outVector[1] = (int) Math.round(maxDistance * yVector); } - private static float calculateMaxDistance(View sceneRoot, int focalX, int focalY) { + private static double calculateMaxDistance(View sceneRoot, int focalX, int focalY) { int maxX = Math.max(focalX, sceneRoot.getWidth() - focalX); int maxY = Math.max(focalY, sceneRoot.getHeight() - focalY); - return calculateDistance(maxX, maxY); + return Math.hypot(maxX, maxY); } - private static float calculateDistance(float x, float y) { - return FloatMath.sqrt((x * x) + (y * y)); - } } diff --git a/core/java/android/transition/PatternPathMotion.java b/core/java/android/transition/PatternPathMotion.java index a609df6..773c387 100644 --- a/core/java/android/transition/PatternPathMotion.java +++ b/core/java/android/transition/PatternPathMotion.java @@ -23,7 +23,6 @@ import android.graphics.Matrix; import android.graphics.Path; import android.graphics.PathMeasure; import android.util.AttributeSet; -import android.util.FloatMath; import android.util.PathParser; /** @@ -119,7 +118,7 @@ public class PatternPathMotion extends PathMotion { mTempMatrix.setTranslate(-startX, -startY); float dx = endX - startX; float dy = endY - startY; - float distance = distance(dx, dy); + float distance = (float) Math.hypot(dx, dy); float scale = 1 / distance; mTempMatrix.postScale(scale, scale); double angle = Math.atan2(dy, dx); @@ -130,9 +129,9 @@ public class PatternPathMotion extends PathMotion { @Override public Path getPath(float startX, float startY, float endX, float endY) { - float dx = endX - startX; - float dy = endY - startY; - float length = distance(dx, dy); + double dx = endX - startX; + double dy = endY - startY; + float length = (float) Math.hypot(dx, dy); double angle = Math.atan2(dy, dx); mTempMatrix.setScale(length, length); @@ -143,7 +142,4 @@ public class PatternPathMotion extends PathMotion { return path; } - private static float distance(float x, float y) { - return FloatMath.sqrt((x * x) + (y * y)); - } } diff --git a/core/java/android/transition/SidePropagation.java b/core/java/android/transition/SidePropagation.java index ad6c2dd..5dd1fff 100644 --- a/core/java/android/transition/SidePropagation.java +++ b/core/java/android/transition/SidePropagation.java @@ -16,7 +16,6 @@ package android.transition; import android.graphics.Rect; -import android.util.FloatMath; import android.util.Log; import android.view.Gravity; import android.view.View; diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java index 2705bcf..c942042 100644 --- a/core/java/android/transition/Transition.java +++ b/core/java/android/transition/Transition.java @@ -1762,7 +1762,17 @@ public abstract class Transition implements Cloneable { runAnimators(); } - boolean areValuesChanged(TransitionValues oldValues, TransitionValues newValues) { + /** + * Returns whether transition values have changed between the start scene and the end scene + * (thus determining whether animation is required). The default implementation compares the + * property values returned from {@link #getTransitionProperties()}, or all property values if + * {@code getTransitionProperties()} returns null. Subclasses may override this method to + * provide logic more specific to their transition implementation. + * + * @param oldValues the first set of values, may be {@code null} + * @param newValues the second set of values, may be {@code null} + */ + protected boolean areValuesChanged(TransitionValues oldValues, TransitionValues newValues) { boolean valuesChanged = false; // if oldValues null, then transition didn't care to stash values, // and won't get canceled diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java index 8779229..26dca43 100644 --- a/core/java/android/transition/Visibility.java +++ b/core/java/android/transition/Visibility.java @@ -182,7 +182,7 @@ public abstract class Visibility extends Transition { return visibility == View.VISIBLE && parent != null; } - private VisibilityInfo getVisibilityChangeInfo(TransitionValues startValues, + private static VisibilityInfo getVisibilityChangeInfo(TransitionValues startValues, TransitionValues endValues) { final VisibilityInfo visInfo = new VisibilityInfo(); visInfo.visibilityChange = false; @@ -484,7 +484,7 @@ public abstract class Visibility extends Transition { } @Override - boolean areValuesChanged(TransitionValues oldValues, TransitionValues newValues) { + protected boolean areValuesChanged(TransitionValues oldValues, TransitionValues newValues) { if (oldValues == null && newValues == null) { return false; } diff --git a/core/java/android/util/AtomicFile.java b/core/java/android/util/AtomicFile.java index a6466fc..3aa3447 100644 --- a/core/java/android/util/AtomicFile.java +++ b/core/java/android/util/AtomicFile.java @@ -102,7 +102,7 @@ public class AtomicFile { str = new FileOutputStream(mBaseName); } catch (FileNotFoundException e) { File parent = mBaseName.getParentFile(); - if (!parent.mkdir()) { + if (!parent.mkdirs()) { throw new IOException("Couldn't create directory " + mBaseName); } FileUtils.setPermissions( diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java index c855e57..d0e5b9e 100644 --- a/core/java/android/util/DisplayMetrics.java +++ b/core/java/android/util/DisplayMetrics.java @@ -62,6 +62,13 @@ public class DisplayMetrics { public static final int DENSITY_HIGH = 240; /** + * Intermediate density for screens that sit between {@link #DENSITY_HIGH} (240dpi) and + * {@link #DENSITY_XHIGH} (320dpi). This is not a density that applications should target, + * instead relying on the system to scale their {@link #DENSITY_XHIGH} assets for them. + */ + public static final int DENSITY_280 = 280; + + /** * Standard quantized DPI for extra-high-density screens. */ public static final int DENSITY_XHIGH = 320; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 04aafc1..ad5d651 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -17109,6 +17109,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @param id The id to search for. * @return The view that has the given id in the hierarchy or null */ + @Nullable public final View findViewById(int id) { if (id < 0) { return null; diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index e1cee1e..504a758 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -1208,7 +1208,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * {@inheritDoc} */ public void bringChildToFront(View child) { - int index = indexOfChild(child); + final int index = indexOfChild(child); if (index >= 0) { removeFromArray(index); addInArray(child, mChildrenCount); @@ -3771,7 +3771,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p> * * @param child the child view to add - * @param index the position at which to add the child + * @param index the position at which to add the child or -1 to add last * @param params the layout parameters to set on the child */ public void addView(View child, int index, LayoutParams params) { @@ -3887,7 +3887,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * If index is negative, it means put it at the end of the list. * * @param child the view to add to the group - * @param index the index at which the child must be added + * @param index the index at which the child must be added or -1 to add last * @param params the layout parameters to associate with the child * @return true if the child was added, false otherwise */ @@ -3902,7 +3902,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * If index is negative, it means put it at the end of the list. * * @param child the view to add to the group - * @param index the index at which the child must be added + * @param index the index at which the child must be added or -1 to add last * @param params the layout parameters to associate with the child * @param preventRequestLayout if true, calling this method will not trigger a * layout request on child diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 1456b52..8964862 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -984,6 +984,7 @@ public abstract class Window { * * @return The view if found or null otherwise. */ + @Nullable public View findViewById(int id) { return getDecorView().findViewById(id); } diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java index 52912b1..d0dde00 100644 --- a/core/java/android/view/accessibility/AccessibilityCache.java +++ b/core/java/android/view/accessibility/AccessibilityCache.java @@ -40,6 +40,9 @@ final class AccessibilityCache { private final Object mLock = new Object(); + private long mAccessibilityFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; + private long mInputFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; + private final SparseArray<AccessibilityWindowInfo> mWindowCache = new SparseArray<>(); @@ -73,9 +76,29 @@ final class AccessibilityCache { synchronized (mLock) { final int eventType = event.getEventType(); switch (eventType) { - case AccessibilityEvent.TYPE_VIEW_FOCUSED: - case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: - case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: + case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: { + if (mAccessibilityFocus != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { + refreshCachedNodeLocked(event.getWindowId(), mAccessibilityFocus); + } + mAccessibilityFocus = event.getSourceNodeId(); + refreshCachedNodeLocked(event.getWindowId(), mAccessibilityFocus); + } break; + + case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: { + if (mAccessibilityFocus == event.getSourceNodeId()) { + refreshCachedNodeLocked(event.getWindowId(), mAccessibilityFocus); + mAccessibilityFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; + } + } break; + + case AccessibilityEvent.TYPE_VIEW_FOCUSED: { + if (mInputFocus != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { + refreshCachedNodeLocked(event.getWindowId(), mInputFocus); + } + mInputFocus = event.getSourceNodeId(); + refreshCachedNodeLocked(event.getWindowId(), mInputFocus); + } break; + case AccessibilityEvent.TYPE_VIEW_SELECTED: case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED: case AccessibilityEvent.TYPE_VIEW_CLICKED: @@ -268,6 +291,9 @@ final class AccessibilityCache { final int windowId = mNodeCache.keyAt(i); clearNodesForWindowLocked(windowId); } + + mAccessibilityFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; + mInputFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; } } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index b56378f..325ffdd 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -494,19 +494,17 @@ public final class InputMethodManager { mIInputContext.finishComposingText(); } catch (RemoteException e) { } - // Check focus again in case that "onWindowFocus" is called before - // handling this message. - if (mServedView != null && mServedView.hasWindowFocus()) { - // "finishComposingText" has been already called above. So we - // should not call mServedInputConnection.finishComposingText here. - // Also, please note that this handler thread could be different - // from a thread that created mServedView. That could happen - // the current activity is running in the system process. - // In that case, we really should not call - // mServedInputConnection.finishComposingText. - if (checkFocusNoStartInput(mHasBeenInactive, false)) { - startInputInner(null, 0, 0, 0); - } + } + // Check focus again in case that "onWindowFocus" is called before + // handling this message. + if (mServedView != null && mServedView.hasWindowFocus()) { + // Please note that this handler thread could be different + // from a thread that created mServedView. That could happen + // the current activity is running in the system process. + // In that case, we really should not call + // mServedInputConnection.finishComposingText. + if (checkFocusNoStartInput(mHasBeenInactive, false)) { + startInputInner(null, 0, 0, 0); } } } diff --git a/core/java/android/webkit/LegacyErrorStrings.java b/core/java/android/webkit/LegacyErrorStrings.java index 11fc05d..60a6ee1 100644 --- a/core/java/android/webkit/LegacyErrorStrings.java +++ b/core/java/android/webkit/LegacyErrorStrings.java @@ -17,7 +17,6 @@ package android.webkit; import android.content.Context; -import android.net.http.EventHandler; import android.util.Log; /** @@ -44,52 +43,52 @@ class LegacyErrorStrings { */ private static int getResource(int errorCode) { switch(errorCode) { - case EventHandler.OK: + case 0: /* EventHandler.OK: */ return com.android.internal.R.string.httpErrorOk; - case EventHandler.ERROR: + case -1: /* EventHandler.ERROR: */ return com.android.internal.R.string.httpError; - case EventHandler.ERROR_LOOKUP: + case -2: /* EventHandler.ERROR_LOOKUP: */ return com.android.internal.R.string.httpErrorLookup; - case EventHandler.ERROR_UNSUPPORTED_AUTH_SCHEME: + case -3: /* EventHandler.ERROR_UNSUPPORTED_AUTH_SCHEME: */ return com.android.internal.R.string.httpErrorUnsupportedAuthScheme; - case EventHandler.ERROR_AUTH: + case -4: /* EventHandler.ERROR_AUTH: */ return com.android.internal.R.string.httpErrorAuth; - case EventHandler.ERROR_PROXYAUTH: + case -5: /* EventHandler.ERROR_PROXYAUTH: */ return com.android.internal.R.string.httpErrorProxyAuth; - case EventHandler.ERROR_CONNECT: + case -6: /* EventHandler.ERROR_CONNECT: */ return com.android.internal.R.string.httpErrorConnect; - case EventHandler.ERROR_IO: + case -7: /* EventHandler.ERROR_IO: */ return com.android.internal.R.string.httpErrorIO; - case EventHandler.ERROR_TIMEOUT: + case -8: /* EventHandler.ERROR_TIMEOUT: */ return com.android.internal.R.string.httpErrorTimeout; - case EventHandler.ERROR_REDIRECT_LOOP: + case -9: /* EventHandler.ERROR_REDIRECT_LOOP: */ return com.android.internal.R.string.httpErrorRedirectLoop; - case EventHandler.ERROR_UNSUPPORTED_SCHEME: + case -10: /* EventHandler.ERROR_UNSUPPORTED_SCHEME: */ return com.android.internal.R.string.httpErrorUnsupportedScheme; - case EventHandler.ERROR_FAILED_SSL_HANDSHAKE: + case -11: /* EventHandler.ERROR_FAILED_SSL_HANDSHAKE: */ return com.android.internal.R.string.httpErrorFailedSslHandshake; - case EventHandler.ERROR_BAD_URL: + case -12: /* EventHandler.ERROR_BAD_URL: */ return com.android.internal.R.string.httpErrorBadUrl; - case EventHandler.FILE_ERROR: + case -13: /* EventHandler.FILE_ERROR: */ return com.android.internal.R.string.httpErrorFile; - case EventHandler.FILE_NOT_FOUND_ERROR: + case -14: /* EventHandler.FILE_NOT_FOUND_ERROR: */ return com.android.internal.R.string.httpErrorFileNotFound; - case EventHandler.TOO_MANY_REQUESTS_ERROR: + case -15: /* EventHandler.TOO_MANY_REQUESTS_ERROR: */ return com.android.internal.R.string.httpErrorTooManyRequests; default: diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java index fede493..f2afeeb 100644 --- a/core/java/android/widget/CompoundButton.java +++ b/core/java/android/widget/CompoundButton.java @@ -16,6 +16,7 @@ package android.widget; +import android.annotation.DrawableRes; import android.annotation.Nullable; import android.graphics.PorterDuff; import com.android.internal.R; @@ -50,7 +51,6 @@ import android.view.accessibility.AccessibilityNodeInfo; */ public abstract class CompoundButton extends Button implements Checkable { private boolean mChecked; - private int mButtonResource; private boolean mBroadcasting; private Drawable mButtonDrawable; @@ -197,54 +197,62 @@ public abstract class CompoundButton extends Button implements Checkable { } /** - * Set the button graphic to a given Drawable, identified by its resource - * id. + * Sets a drawable as the compound button image given its resource + * identifier. * - * @param resid the resource id of the drawable to use as the button - * graphic + * @param resId the resource identifier of the drawable + * @attr ref android.R.styleable#CompoundButton_button */ - public void setButtonDrawable(int resid) { - if (resid != 0 && resid == mButtonResource) { - return; - } - - mButtonResource = resid; - - Drawable d = null; - if (mButtonResource != 0) { - d = getContext().getDrawable(mButtonResource); + public void setButtonDrawable(@DrawableRes int resId) { + final Drawable d; + if (resId != 0) { + d = getContext().getDrawable(resId); + } else { + d = null; } setButtonDrawable(d); } /** - * Set the button graphic to a given Drawable + * Sets a drawable as the compound button image. * - * @param d The Drawable to use as the button graphic + * @param drawable the drawable to set + * @attr ref android.R.styleable#CompoundButton_button */ - public void setButtonDrawable(Drawable d) { - if (mButtonDrawable != d) { + @Nullable + public void setButtonDrawable(@Nullable Drawable drawable) { + if (mButtonDrawable != drawable) { if (mButtonDrawable != null) { mButtonDrawable.setCallback(null); unscheduleDrawable(mButtonDrawable); } - mButtonDrawable = d; + mButtonDrawable = drawable; - if (d != null) { - d.setCallback(this); - d.setLayoutDirection(getLayoutDirection()); - if (d.isStateful()) { - d.setState(getDrawableState()); + if (drawable != null) { + drawable.setCallback(this); + drawable.setLayoutDirection(getLayoutDirection()); + if (drawable.isStateful()) { + drawable.setState(getDrawableState()); } - d.setVisible(getVisibility() == VISIBLE, false); - setMinHeight(d.getIntrinsicHeight()); + drawable.setVisible(getVisibility() == VISIBLE, false); + setMinHeight(drawable.getIntrinsicHeight()); applyButtonTint(); } } } /** + * @return the drawable used as the compound button image + * @see #setButtonDrawable(Drawable) + * @see #setButtonDrawable(int) + */ + @Nullable + public Drawable getButtonDrawable() { + return mButtonDrawable; + } + + /** * Applies a tint to the button drawable. Does not modify the current tint * mode, which is {@link PorterDuff.Mode#SRC_IN} by default. * <p> diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java index 6925756..391347e 100644 --- a/core/java/android/widget/EdgeEffect.java +++ b/core/java/android/widget/EdgeEffect.java @@ -24,7 +24,6 @@ import android.graphics.Rect; import android.content.Context; import android.graphics.Canvas; -import android.util.FloatMath; import android.view.animation.AnimationUtils; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; @@ -220,8 +219,8 @@ public class EdgeEffect { if (mPullDistance == 0) { mGlowScaleY = mGlowScaleYStart = 0; } else { - final float scale = Math.max(0, 1 - 1 / - FloatMath.sqrt(Math.abs(mPullDistance) * mBounds.height()) - 0.3f) / 0.7f; + final float scale = (float) (Math.max(0, 1 - 1 / + Math.sqrt(Math.abs(mPullDistance) * mBounds.height()) - 0.3d) / 0.7d); mGlowScaleY = mGlowScaleYStart = scale; } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index d5166f3..4752594 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -1352,6 +1352,9 @@ public class Editor { searchStartIndex); // Note how dynamic layout's internal block indices get updated from Editor blockIndices[i] = blockIndex; + if (mTextDisplayLists[blockIndex] != null) { + mTextDisplayLists[blockIndex].isDirty = true; + } searchStartIndex = blockIndex + 1; } @@ -1388,6 +1391,7 @@ public class Editor { // brings this range of text back to the top left corner of the viewport hardwareCanvas.translate(-left, -top); layout.drawText(hardwareCanvas, blockBeginLine, blockEndLine); + mTextDisplayLists[blockIndex].isDirty = false; // No need to untranslate, previous context is popped after // drawDisplayList } finally { diff --git a/core/java/com/android/internal/http/multipart/ByteArrayPartSource.java b/core/java/com/android/internal/http/multipart/ByteArrayPartSource.java deleted file mode 100644 index faaac7f..0000000 --- a/core/java/com/android/internal/http/multipart/ByteArrayPartSource.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/ByteArrayPartSource.java,v 1.7 2004/04/18 23:51:37 jsdever Exp $ - * $Revision: 480424 $ - * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $ - * - * ==================================================================== - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * <http://www.apache.org/>. - * - */ - -package com.android.internal.http.multipart; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; - -/** - * A PartSource that reads from a byte array. This class should be used when - * the data to post is already loaded into memory. - * - * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a> - * - * @since 2.0 - */ -public class ByteArrayPartSource implements PartSource { - - /** Name of the source file. */ - private String fileName; - - /** Byte array of the source file. */ - private byte[] bytes; - - /** - * Constructor for ByteArrayPartSource. - * - * @param fileName the name of the file these bytes represent - * @param bytes the content of this part - */ - public ByteArrayPartSource(String fileName, byte[] bytes) { - - this.fileName = fileName; - this.bytes = bytes; - - } - - /** - * @see PartSource#getLength() - */ - public long getLength() { - return bytes.length; - } - - /** - * @see PartSource#getFileName() - */ - public String getFileName() { - return fileName; - } - - /** - * @see PartSource#createInputStream() - */ - public InputStream createInputStream() { - return new ByteArrayInputStream(bytes); - } - -} diff --git a/core/java/com/android/internal/http/multipart/FilePart.java b/core/java/com/android/internal/http/multipart/FilePart.java deleted file mode 100644 index 45e4be6..0000000 --- a/core/java/com/android/internal/http/multipart/FilePart.java +++ /dev/null @@ -1,259 +0,0 @@ -/* - * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/FilePart.java,v 1.19 2004/04/18 23:51:37 jsdever Exp $ - * $Revision: 480424 $ - * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $ - * - * ==================================================================== - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * <http://www.apache.org/>. - * - */ - -package com.android.internal.http.multipart; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import org.apache.http.util.EncodingUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * This class implements a part of a Multipart post object that - * consists of a file. - * - * @author <a href="mailto:mattalbright@yahoo.com">Matthew Albright</a> - * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a> - * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a> - * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a> - * @author <a href="mailto:mdiggory@latte.harvard.edu">Mark Diggory</a> - * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a> - * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a> - * - * @since 2.0 - * - * @deprecated Please use {@link java.net.URLConnection} and friends instead. - * The Apache HTTP client is no longer maintained and may be removed in a future - * release. Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a> - * for further details. - */ -@Deprecated -public class FilePart extends PartBase { - - /** Default content encoding of file attachments. */ - public static final String DEFAULT_CONTENT_TYPE = "application/octet-stream"; - - /** Default charset of file attachments. */ - public static final String DEFAULT_CHARSET = "ISO-8859-1"; - - /** Default transfer encoding of file attachments. */ - public static final String DEFAULT_TRANSFER_ENCODING = "binary"; - - /** Log object for this class. */ - private static final Log LOG = LogFactory.getLog(FilePart.class); - - /** Attachment's file name */ - protected static final String FILE_NAME = "; filename="; - - /** Attachment's file name as a byte array */ - private static final byte[] FILE_NAME_BYTES = - EncodingUtils.getAsciiBytes(FILE_NAME); - - /** Source of the file part. */ - private PartSource source; - - /** - * FilePart Constructor. - * - * @param name the name for this part - * @param partSource the source for this part - * @param contentType the content type for this part, if <code>null</code> the - * {@link #DEFAULT_CONTENT_TYPE default} is used - * @param charset the charset encoding for this part, if <code>null</code> the - * {@link #DEFAULT_CHARSET default} is used - */ - public FilePart(String name, PartSource partSource, String contentType, String charset) { - - super( - name, - contentType == null ? DEFAULT_CONTENT_TYPE : contentType, - charset == null ? "ISO-8859-1" : charset, - DEFAULT_TRANSFER_ENCODING - ); - - if (partSource == null) { - throw new IllegalArgumentException("Source may not be null"); - } - this.source = partSource; - } - - /** - * FilePart Constructor. - * - * @param name the name for this part - * @param partSource the source for this part - */ - public FilePart(String name, PartSource partSource) { - this(name, partSource, null, null); - } - - /** - * FilePart Constructor. - * - * @param name the name of the file part - * @param file the file to post - * - * @throws FileNotFoundException if the <i>file</i> is not a normal - * file or if it is not readable. - */ - public FilePart(String name, File file) - throws FileNotFoundException { - this(name, new FilePartSource(file), null, null); - } - - /** - * FilePart Constructor. - * - * @param name the name of the file part - * @param file the file to post - * @param contentType the content type for this part, if <code>null</code> the - * {@link #DEFAULT_CONTENT_TYPE default} is used - * @param charset the charset encoding for this part, if <code>null</code> the - * {@link #DEFAULT_CHARSET default} is used - * - * @throws FileNotFoundException if the <i>file</i> is not a normal - * file or if it is not readable. - */ - public FilePart(String name, File file, String contentType, String charset) - throws FileNotFoundException { - this(name, new FilePartSource(file), contentType, charset); - } - - /** - * FilePart Constructor. - * - * @param name the name of the file part - * @param fileName the file name - * @param file the file to post - * - * @throws FileNotFoundException if the <i>file</i> is not a normal - * file or if it is not readable. - */ - public FilePart(String name, String fileName, File file) - throws FileNotFoundException { - this(name, new FilePartSource(fileName, file), null, null); - } - - /** - * FilePart Constructor. - * - * @param name the name of the file part - * @param fileName the file name - * @param file the file to post - * @param contentType the content type for this part, if <code>null</code> the - * {@link #DEFAULT_CONTENT_TYPE default} is used - * @param charset the charset encoding for this part, if <code>null</code> the - * {@link #DEFAULT_CHARSET default} is used - * - * @throws FileNotFoundException if the <i>file</i> is not a normal - * file or if it is not readable. - */ - public FilePart(String name, String fileName, File file, String contentType, String charset) - throws FileNotFoundException { - this(name, new FilePartSource(fileName, file), contentType, charset); - } - - /** - * Write the disposition header to the output stream - * @param out The output stream - * @throws IOException If an IO problem occurs - * @see Part#sendDispositionHeader(OutputStream) - */ - @Override - protected void sendDispositionHeader(OutputStream out) - throws IOException { - LOG.trace("enter sendDispositionHeader(OutputStream out)"); - super.sendDispositionHeader(out); - String filename = this.source.getFileName(); - if (filename != null) { - out.write(FILE_NAME_BYTES); - out.write(QUOTE_BYTES); - out.write(EncodingUtils.getAsciiBytes(filename)); - out.write(QUOTE_BYTES); - } - } - - /** - * Write the data in "source" to the specified stream. - * @param out The output stream. - * @throws IOException if an IO problem occurs. - * @see Part#sendData(OutputStream) - */ - @Override - protected void sendData(OutputStream out) throws IOException { - LOG.trace("enter sendData(OutputStream out)"); - if (lengthOfData() == 0) { - - // this file contains no data, so there is nothing to send. - // we don't want to create a zero length buffer as this will - // cause an infinite loop when reading. - LOG.debug("No data to send."); - return; - } - - byte[] tmp = new byte[4096]; - InputStream instream = source.createInputStream(); - try { - int len; - while ((len = instream.read(tmp)) >= 0) { - out.write(tmp, 0, len); - } - } finally { - // we're done with the stream, close it - instream.close(); - } - } - - /** - * Returns the source of the file part. - * - * @return The source. - */ - protected PartSource getSource() { - LOG.trace("enter getSource()"); - return this.source; - } - - /** - * Return the length of the data. - * @return The length. - * @see Part#lengthOfData() - */ - @Override - protected long lengthOfData() { - LOG.trace("enter lengthOfData()"); - return source.getLength(); - } - -} diff --git a/core/java/com/android/internal/http/multipart/FilePartSource.java b/core/java/com/android/internal/http/multipart/FilePartSource.java deleted file mode 100644 index eb5cc0f..0000000 --- a/core/java/com/android/internal/http/multipart/FilePartSource.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/FilePartSource.java,v 1.10 2004/04/18 23:51:37 jsdever Exp $ - * $Revision: 480424 $ - * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $ - * - * ==================================================================== - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * <http://www.apache.org/>. - * - */ - -package com.android.internal.http.multipart; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; - -/** - * A PartSource that reads from a File. - * - * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a> - * @author <a href="mailto:mdiggory@latte.harvard.edu">Mark Diggory</a> - * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a> - * - * @since 2.0 - */ -public class FilePartSource implements PartSource { - - /** File part file. */ - private File file = null; - - /** File part file name. */ - private String fileName = null; - - /** - * Constructor for FilePartSource. - * - * @param file the FilePart source File. - * - * @throws FileNotFoundException if the file does not exist or - * cannot be read - */ - public FilePartSource(File file) throws FileNotFoundException { - this.file = file; - if (file != null) { - if (!file.isFile()) { - throw new FileNotFoundException("File is not a normal file."); - } - if (!file.canRead()) { - throw new FileNotFoundException("File is not readable."); - } - this.fileName = file.getName(); - } - } - - /** - * Constructor for FilePartSource. - * - * @param fileName the file name of the FilePart - * @param file the source File for the FilePart - * - * @throws FileNotFoundException if the file does not exist or - * cannot be read - */ - public FilePartSource(String fileName, File file) - throws FileNotFoundException { - this(file); - if (fileName != null) { - this.fileName = fileName; - } - } - - /** - * Return the length of the file - * @return the length of the file. - * @see PartSource#getLength() - */ - public long getLength() { - if (this.file != null) { - return this.file.length(); - } else { - return 0; - } - } - - /** - * Return the current filename - * @return the filename. - * @see PartSource#getFileName() - */ - public String getFileName() { - return (fileName == null) ? "noname" : fileName; - } - - /** - * Return a new {@link FileInputStream} for the current filename. - * @return the new input stream. - * @throws IOException If an IO problem occurs. - * @see PartSource#createInputStream() - */ - public InputStream createInputStream() throws IOException { - if (this.file != null) { - return new FileInputStream(this.file); - } else { - return new ByteArrayInputStream(new byte[] {}); - } - } - -} diff --git a/core/java/com/android/internal/http/multipart/MultipartEntity.java b/core/java/com/android/internal/http/multipart/MultipartEntity.java deleted file mode 100644 index 5319251..0000000 --- a/core/java/com/android/internal/http/multipart/MultipartEntity.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/MultipartRequestEntity.java,v 1.1 2004/10/06 03:39:59 mbecke Exp $ - * $Revision: 502647 $ - * $Date: 2007-02-02 17:22:54 +0100 (Fri, 02 Feb 2007) $ - * - * ==================================================================== - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * <http://www.apache.org/>. - * - */ - -package com.android.internal.http.multipart; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Random; - -import org.apache.http.Header; -import org.apache.http.entity.AbstractHttpEntity; -import org.apache.http.message.BasicHeader; -import org.apache.http.params.HttpParams; -import org.apache.http.protocol.HTTP; -import org.apache.http.util.EncodingUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Implements a request entity suitable for an HTTP multipart POST method. - * <p> - * The HTTP multipart POST method is defined in section 3.3 of - * <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC1867</a>: - * <blockquote> - * The media-type multipart/form-data follows the rules of all multipart - * MIME data streams as outlined in RFC 1521. The multipart/form-data contains - * a series of parts. Each part is expected to contain a content-disposition - * header where the value is "form-data" and a name attribute specifies - * the field name within the form, e.g., 'content-disposition: form-data; - * name="xxxxx"', where xxxxx is the field name corresponding to that field. - * Field names originally in non-ASCII character sets may be encoded using - * the method outlined in RFC 1522. - * </blockquote> - * </p> - * <p>This entity is designed to be used in conjunction with the - * {@link org.apache.http.HttpRequest} to provide - * multipart posts. Example usage:</p> - * <pre> - * File f = new File("/path/fileToUpload.txt"); - * HttpRequest request = new HttpRequest("http://host/some_path"); - * Part[] parts = { - * new StringPart("param_name", "value"), - * new FilePart(f.getName(), f) - * }; - * filePost.setEntity( - * new MultipartRequestEntity(parts, filePost.getParams()) - * ); - * HttpClient client = new HttpClient(); - * int status = client.executeMethod(filePost); - * </pre> - * - * @since 3.0 - * - * @deprecated Please use {@link java.net.URLConnection} and friends instead. - * The Apache HTTP client is no longer maintained and may be removed in a future - * release. Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a> - * for further details. - */ -@Deprecated -public class MultipartEntity extends AbstractHttpEntity { - - private static final Log log = LogFactory.getLog(MultipartEntity.class); - - /** The Content-Type for multipart/form-data. */ - private static final String MULTIPART_FORM_CONTENT_TYPE = "multipart/form-data"; - - /** - * Sets the value to use as the multipart boundary. - * <p> - * This parameter expects a value if type {@link String}. - * </p> - */ - public static final String MULTIPART_BOUNDARY = "http.method.multipart.boundary"; - - /** - * The pool of ASCII chars to be used for generating a multipart boundary. - */ - private static byte[] MULTIPART_CHARS = EncodingUtils.getAsciiBytes( - "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); - - /** - * Generates a random multipart boundary string. - */ - private static byte[] generateMultipartBoundary() { - Random rand = new Random(); - byte[] bytes = new byte[rand.nextInt(11) + 30]; // a random size from 30 to 40 - for (int i = 0; i < bytes.length; i++) { - bytes[i] = MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]; - } - return bytes; - } - - /** The MIME parts as set by the constructor */ - protected Part[] parts; - - private byte[] multipartBoundary; - - private HttpParams params; - - private boolean contentConsumed = false; - - /** - * Creates a new multipart entity containing the given parts. - * @param parts The parts to include. - * @param params The params of the HttpMethod using this entity. - */ - public MultipartEntity(Part[] parts, HttpParams params) { - if (parts == null) { - throw new IllegalArgumentException("parts cannot be null"); - } - if (params == null) { - throw new IllegalArgumentException("params cannot be null"); - } - this.parts = parts; - this.params = params; - } - - public MultipartEntity(Part[] parts) { - setContentType(MULTIPART_FORM_CONTENT_TYPE); - if (parts == null) { - throw new IllegalArgumentException("parts cannot be null"); - } - this.parts = parts; - this.params = null; - } - - /** - * Returns the MIME boundary string that is used to demarcate boundaries of - * this part. The first call to this method will implicitly create a new - * boundary string. To create a boundary string first the - * HttpMethodParams.MULTIPART_BOUNDARY parameter is considered. Otherwise - * a random one is generated. - * - * @return The boundary string of this entity in ASCII encoding. - */ - protected byte[] getMultipartBoundary() { - if (multipartBoundary == null) { - String temp = null; - if (params != null) { - temp = (String) params.getParameter(MULTIPART_BOUNDARY); - } - if (temp != null) { - multipartBoundary = EncodingUtils.getAsciiBytes(temp); - } else { - multipartBoundary = generateMultipartBoundary(); - } - } - return multipartBoundary; - } - - /** - * Returns <code>true</code> if all parts are repeatable, <code>false</code> otherwise. - */ - public boolean isRepeatable() { - for (int i = 0; i < parts.length; i++) { - if (!parts[i].isRepeatable()) { - return false; - } - } - return true; - } - - /* (non-Javadoc) - */ - public void writeTo(OutputStream out) throws IOException { - Part.sendParts(out, parts, getMultipartBoundary()); - } - /* (non-Javadoc) - * @see org.apache.commons.http.AbstractHttpEntity.#getContentType() - */ - @Override - public Header getContentType() { - StringBuffer buffer = new StringBuffer(MULTIPART_FORM_CONTENT_TYPE); - buffer.append("; boundary="); - buffer.append(EncodingUtils.getAsciiString(getMultipartBoundary())); - return new BasicHeader(HTTP.CONTENT_TYPE, buffer.toString()); - - } - - /* (non-Javadoc) - */ - public long getContentLength() { - try { - return Part.getLengthOfParts(parts, getMultipartBoundary()); - } catch (Exception e) { - log.error("An exception occurred while getting the length of the parts", e); - return 0; - } - } - - public InputStream getContent() throws IOException, IllegalStateException { - if(!isRepeatable() && this.contentConsumed ) { - throw new IllegalStateException("Content has been consumed"); - } - this.contentConsumed = true; - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - Part.sendParts(baos, this.parts, this.multipartBoundary); - ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); - return bais; - } - - public boolean isStreaming() { - return false; - } -} diff --git a/core/java/com/android/internal/http/multipart/Part.java b/core/java/com/android/internal/http/multipart/Part.java deleted file mode 100644 index 1d66dc6..0000000 --- a/core/java/com/android/internal/http/multipart/Part.java +++ /dev/null @@ -1,445 +0,0 @@ -/* - * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/Part.java,v 1.16 2005/01/14 21:16:40 olegk Exp $ - * $Revision: 480424 $ - * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $ - * - * ==================================================================== - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * <http://www.apache.org/>. - * - */ - -package com.android.internal.http.multipart; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; - -import org.apache.http.util.EncodingUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Abstract class for one Part of a multipart post object. - * - * @author <a href="mailto:mattalbright@yahoo.com">Matthew Albright</a> - * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a> - * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a> - * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a> - * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a> - * - * @since 2.0 - * - * @deprecated Please use {@link java.net.URLConnection} and friends instead. - * The Apache HTTP client is no longer maintained and may be removed in a future - * release. Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a> - * for further details. - */ -@Deprecated -public abstract class Part { - - /** Log object for this class. */ - private static final Log LOG = LogFactory.getLog(Part.class); - - /** - * The boundary - * @deprecated use {@link org.apache.http.client.methods.multipart#MULTIPART_BOUNDARY} - */ - protected static final String BOUNDARY = "----------------314159265358979323846"; - - /** - * The boundary as a byte array. - * @deprecated - */ - protected static final byte[] BOUNDARY_BYTES = EncodingUtils.getAsciiBytes(BOUNDARY); - - /** - * The default boundary to be used if {@link #setPartBoundary(byte[])} has not - * been called. - */ - private static final byte[] DEFAULT_BOUNDARY_BYTES = BOUNDARY_BYTES; - - /** Carriage return/linefeed */ - protected static final String CRLF = "\r\n"; - - /** Carriage return/linefeed as a byte array */ - protected static final byte[] CRLF_BYTES = EncodingUtils.getAsciiBytes(CRLF); - - /** Content dispostion characters */ - protected static final String QUOTE = "\""; - - /** Content dispostion as a byte array */ - protected static final byte[] QUOTE_BYTES = - EncodingUtils.getAsciiBytes(QUOTE); - - /** Extra characters */ - protected static final String EXTRA = "--"; - - /** Extra characters as a byte array */ - protected static final byte[] EXTRA_BYTES = - EncodingUtils.getAsciiBytes(EXTRA); - - /** Content dispostion characters */ - protected static final String CONTENT_DISPOSITION = "Content-Disposition: form-data; name="; - - /** Content dispostion as a byte array */ - protected static final byte[] CONTENT_DISPOSITION_BYTES = - EncodingUtils.getAsciiBytes(CONTENT_DISPOSITION); - - /** Content type header */ - protected static final String CONTENT_TYPE = "Content-Type: "; - - /** Content type header as a byte array */ - protected static final byte[] CONTENT_TYPE_BYTES = - EncodingUtils.getAsciiBytes(CONTENT_TYPE); - - /** Content charset */ - protected static final String CHARSET = "; charset="; - - /** Content charset as a byte array */ - protected static final byte[] CHARSET_BYTES = - EncodingUtils.getAsciiBytes(CHARSET); - - /** Content type header */ - protected static final String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding: "; - - /** Content type header as a byte array */ - protected static final byte[] CONTENT_TRANSFER_ENCODING_BYTES = - EncodingUtils.getAsciiBytes(CONTENT_TRANSFER_ENCODING); - - /** - * Return the boundary string. - * @return the boundary string - * @deprecated uses a constant string. Rather use {@link #getPartBoundary} - */ - public static String getBoundary() { - return BOUNDARY; - } - - /** - * The ASCII bytes to use as the multipart boundary. - */ - private byte[] boundaryBytes; - - /** - * Return the name of this part. - * @return The name. - */ - public abstract String getName(); - - /** - * Returns the content type of this part. - * @return the content type, or <code>null</code> to exclude the content type header - */ - public abstract String getContentType(); - - /** - * Return the character encoding of this part. - * @return the character encoding, or <code>null</code> to exclude the character - * encoding header - */ - public abstract String getCharSet(); - - /** - * Return the transfer encoding of this part. - * @return the transfer encoding, or <code>null</code> to exclude the transfer encoding header - */ - public abstract String getTransferEncoding(); - - /** - * Gets the part boundary to be used. - * @return the part boundary as an array of bytes. - * - * @since 3.0 - */ - protected byte[] getPartBoundary() { - if (boundaryBytes == null) { - // custom boundary bytes have not been set, use the default. - return DEFAULT_BOUNDARY_BYTES; - } else { - return boundaryBytes; - } - } - - /** - * Sets the part boundary. Only meant to be used by - * {@link Part#sendParts(OutputStream, Part[], byte[])} - * and {@link Part#getLengthOfParts(Part[], byte[])} - * @param boundaryBytes An array of ASCII bytes. - * @since 3.0 - */ - void setPartBoundary(byte[] boundaryBytes) { - this.boundaryBytes = boundaryBytes; - } - - /** - * Tests if this part can be sent more than once. - * @return <code>true</code> if {@link #sendData(OutputStream)} can be successfully called - * more than once. - * @since 3.0 - */ - public boolean isRepeatable() { - return true; - } - - /** - * Write the start to the specified output stream - * @param out The output stream - * @throws IOException If an IO problem occurs. - */ - protected void sendStart(OutputStream out) throws IOException { - LOG.trace("enter sendStart(OutputStream out)"); - out.write(EXTRA_BYTES); - out.write(getPartBoundary()); - out.write(CRLF_BYTES); - } - - /** - * Write the content disposition header to the specified output stream - * - * @param out The output stream - * @throws IOException If an IO problem occurs. - */ - protected void sendDispositionHeader(OutputStream out) throws IOException { - LOG.trace("enter sendDispositionHeader(OutputStream out)"); - out.write(CONTENT_DISPOSITION_BYTES); - out.write(QUOTE_BYTES); - out.write(EncodingUtils.getAsciiBytes(getName())); - out.write(QUOTE_BYTES); - } - - /** - * Write the content type header to the specified output stream - * @param out The output stream - * @throws IOException If an IO problem occurs. - */ - protected void sendContentTypeHeader(OutputStream out) throws IOException { - LOG.trace("enter sendContentTypeHeader(OutputStream out)"); - String contentType = getContentType(); - if (contentType != null) { - out.write(CRLF_BYTES); - out.write(CONTENT_TYPE_BYTES); - out.write(EncodingUtils.getAsciiBytes(contentType)); - String charSet = getCharSet(); - if (charSet != null) { - out.write(CHARSET_BYTES); - out.write(EncodingUtils.getAsciiBytes(charSet)); - } - } - } - - /** - * Write the content transfer encoding header to the specified - * output stream - * - * @param out The output stream - * @throws IOException If an IO problem occurs. - */ - protected void sendTransferEncodingHeader(OutputStream out) throws IOException { - LOG.trace("enter sendTransferEncodingHeader(OutputStream out)"); - String transferEncoding = getTransferEncoding(); - if (transferEncoding != null) { - out.write(CRLF_BYTES); - out.write(CONTENT_TRANSFER_ENCODING_BYTES); - out.write(EncodingUtils.getAsciiBytes(transferEncoding)); - } - } - - /** - * Write the end of the header to the output stream - * @param out The output stream - * @throws IOException If an IO problem occurs. - */ - protected void sendEndOfHeader(OutputStream out) throws IOException { - LOG.trace("enter sendEndOfHeader(OutputStream out)"); - out.write(CRLF_BYTES); - out.write(CRLF_BYTES); - } - - /** - * Write the data to the specified output stream - * @param out The output stream - * @throws IOException If an IO problem occurs. - */ - protected abstract void sendData(OutputStream out) throws IOException; - - /** - * Return the length of the main content - * - * @return long The length. - * @throws IOException If an IO problem occurs - */ - protected abstract long lengthOfData() throws IOException; - - /** - * Write the end data to the output stream. - * @param out The output stream - * @throws IOException If an IO problem occurs. - */ - protected void sendEnd(OutputStream out) throws IOException { - LOG.trace("enter sendEnd(OutputStream out)"); - out.write(CRLF_BYTES); - } - - /** - * Write all the data to the output stream. - * If you override this method make sure to override - * #length() as well - * - * @param out The output stream - * @throws IOException If an IO problem occurs. - */ - public void send(OutputStream out) throws IOException { - LOG.trace("enter send(OutputStream out)"); - sendStart(out); - sendDispositionHeader(out); - sendContentTypeHeader(out); - sendTransferEncodingHeader(out); - sendEndOfHeader(out); - sendData(out); - sendEnd(out); - } - - - /** - * Return the full length of all the data. - * If you override this method make sure to override - * #send(OutputStream) as well - * - * @return long The length. - * @throws IOException If an IO problem occurs - */ - public long length() throws IOException { - LOG.trace("enter length()"); - if (lengthOfData() < 0) { - return -1; - } - ByteArrayOutputStream overhead = new ByteArrayOutputStream(); - sendStart(overhead); - sendDispositionHeader(overhead); - sendContentTypeHeader(overhead); - sendTransferEncodingHeader(overhead); - sendEndOfHeader(overhead); - sendEnd(overhead); - return overhead.size() + lengthOfData(); - } - - /** - * Return a string representation of this object. - * @return A string representation of this object. - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return this.getName(); - } - - /** - * Write all parts and the last boundary to the specified output stream. - * - * @param out The stream to write to. - * @param parts The parts to write. - * - * @throws IOException If an I/O error occurs while writing the parts. - */ - public static void sendParts(OutputStream out, final Part[] parts) - throws IOException { - sendParts(out, parts, DEFAULT_BOUNDARY_BYTES); - } - - /** - * Write all parts and the last boundary to the specified output stream. - * - * @param out The stream to write to. - * @param parts The parts to write. - * @param partBoundary The ASCII bytes to use as the part boundary. - * - * @throws IOException If an I/O error occurs while writing the parts. - * - * @since 3.0 - */ - public static void sendParts(OutputStream out, Part[] parts, byte[] partBoundary) - throws IOException { - - if (parts == null) { - throw new IllegalArgumentException("Parts may not be null"); - } - if (partBoundary == null || partBoundary.length == 0) { - throw new IllegalArgumentException("partBoundary may not be empty"); - } - for (int i = 0; i < parts.length; i++) { - // set the part boundary before the part is sent - parts[i].setPartBoundary(partBoundary); - parts[i].send(out); - } - out.write(EXTRA_BYTES); - out.write(partBoundary); - out.write(EXTRA_BYTES); - out.write(CRLF_BYTES); - } - - /** - * Return the total sum of all parts and that of the last boundary - * - * @param parts The parts. - * @return The total length - * - * @throws IOException If an I/O error occurs while writing the parts. - */ - public static long getLengthOfParts(Part[] parts) - throws IOException { - return getLengthOfParts(parts, DEFAULT_BOUNDARY_BYTES); - } - - /** - * Gets the length of the multipart message including the given parts. - * - * @param parts The parts. - * @param partBoundary The ASCII bytes to use as the part boundary. - * @return The total length - * - * @throws IOException If an I/O error occurs while writing the parts. - * - * @since 3.0 - */ - public static long getLengthOfParts(Part[] parts, byte[] partBoundary) throws IOException { - LOG.trace("getLengthOfParts(Parts[])"); - if (parts == null) { - throw new IllegalArgumentException("Parts may not be null"); - } - long total = 0; - for (int i = 0; i < parts.length; i++) { - // set the part boundary before we calculate the part's length - parts[i].setPartBoundary(partBoundary); - long l = parts[i].length(); - if (l < 0) { - return -1; - } - total += l; - } - total += EXTRA_BYTES.length; - total += partBoundary.length; - total += EXTRA_BYTES.length; - total += CRLF_BYTES.length; - return total; - } -} diff --git a/core/java/com/android/internal/http/multipart/PartBase.java b/core/java/com/android/internal/http/multipart/PartBase.java deleted file mode 100644 index 876d15d..0000000 --- a/core/java/com/android/internal/http/multipart/PartBase.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/PartBase.java,v 1.5 2004/04/18 23:51:37 jsdever Exp $ - * $Revision: 480424 $ - * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $ - * - * ==================================================================== - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * <http://www.apache.org/>. - * - */ - -package com.android.internal.http.multipart; - - -/** - * Provides setters and getters for the basic Part properties. - * - * @author Michael Becke - */ -public abstract class PartBase extends Part { - - /** Name of the file part. */ - private String name; - - /** Content type of the file part. */ - private String contentType; - - /** Content encoding of the file part. */ - private String charSet; - - /** The transfer encoding. */ - private String transferEncoding; - - /** - * Constructor. - * - * @param name The name of the part - * @param contentType The content type, or <code>null</code> - * @param charSet The character encoding, or <code>null</code> - * @param transferEncoding The transfer encoding, or <code>null</code> - */ - public PartBase(String name, String contentType, String charSet, String transferEncoding) { - - if (name == null) { - throw new IllegalArgumentException("Name must not be null"); - } - this.name = name; - this.contentType = contentType; - this.charSet = charSet; - this.transferEncoding = transferEncoding; - } - - /** - * Returns the name. - * @return The name. - * @see Part#getName() - */ - @Override - public String getName() { - return this.name; - } - - /** - * Returns the content type of this part. - * @return String The name. - */ - @Override - public String getContentType() { - return this.contentType; - } - - /** - * Return the character encoding of this part. - * @return String The name. - */ - @Override - public String getCharSet() { - return this.charSet; - } - - /** - * Returns the transfer encoding of this part. - * @return String The name. - */ - @Override - public String getTransferEncoding() { - return transferEncoding; - } - - /** - * Sets the character encoding. - * - * @param charSet the character encoding, or <code>null</code> to exclude the character - * encoding header - */ - public void setCharSet(String charSet) { - this.charSet = charSet; - } - - /** - * Sets the content type. - * - * @param contentType the content type, or <code>null</code> to exclude the content type header - */ - public void setContentType(String contentType) { - this.contentType = contentType; - } - - /** - * Sets the part name. - * - * @param name - */ - public void setName(String name) { - if (name == null) { - throw new IllegalArgumentException("Name must not be null"); - } - this.name = name; - } - - /** - * Sets the transfer encoding. - * - * @param transferEncoding the transfer encoding, or <code>null</code> to exclude the - * transfer encoding header - */ - public void setTransferEncoding(String transferEncoding) { - this.transferEncoding = transferEncoding; - } - -} diff --git a/core/java/com/android/internal/http/multipart/PartSource.java b/core/java/com/android/internal/http/multipart/PartSource.java deleted file mode 100644 index 3740696..0000000 --- a/core/java/com/android/internal/http/multipart/PartSource.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/PartSource.java,v 1.6 2004/04/18 23:51:37 jsdever Exp $ - * $Revision: 480424 $ - * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $ - * - * ==================================================================== - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * <http://www.apache.org/>. - * - */ - -package com.android.internal.http.multipart; - -import java.io.IOException; -import java.io.InputStream; - -/** - * An interface for providing access to data when posting MultiPart messages. - * - * @see FilePart - * - * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a> - * - * @since 2.0 - */ -public interface PartSource { - - /** - * Gets the number of bytes contained in this source. - * - * @return a value >= 0 - */ - long getLength(); - - /** - * Gets the name of the file this source represents. - * - * @return the fileName used for posting a MultiPart file part - */ - String getFileName(); - - /** - * Gets a new InputStream for reading this source. This method can be - * called more than once and should therefore return a new stream every - * time. - * - * @return a new InputStream - * - * @throws IOException if an error occurs when creating the InputStream - */ - InputStream createInputStream() throws IOException; - -} diff --git a/core/java/com/android/internal/http/multipart/StringPart.java b/core/java/com/android/internal/http/multipart/StringPart.java deleted file mode 100644 index 73d0f90..0000000 --- a/core/java/com/android/internal/http/multipart/StringPart.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/StringPart.java,v 1.11 2004/04/18 23:51:37 jsdever Exp $ - * $Revision: 480424 $ - * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $ - * - * ==================================================================== - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * <http://www.apache.org/>. - * - */ - -package com.android.internal.http.multipart; - -import java.io.OutputStream; -import java.io.IOException; - -import org.apache.http.util.EncodingUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Simple string parameter for a multipart post - * - * @author <a href="mailto:mattalbright@yahoo.com">Matthew Albright</a> - * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a> - * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a> - * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a> - * - * @since 2.0 - * - * @deprecated Please use {@link java.net.URLConnection} and friends instead. - * The Apache HTTP client is no longer maintained and may be removed in a future - * release. Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a> - * for further details. - */ -@Deprecated -public class StringPart extends PartBase { - - /** Log object for this class. */ - private static final Log LOG = LogFactory.getLog(StringPart.class); - - /** Default content encoding of string parameters. */ - public static final String DEFAULT_CONTENT_TYPE = "text/plain"; - - /** Default charset of string parameters*/ - public static final String DEFAULT_CHARSET = "US-ASCII"; - - /** Default transfer encoding of string parameters*/ - public static final String DEFAULT_TRANSFER_ENCODING = "8bit"; - - /** Contents of this StringPart. */ - private byte[] content; - - /** The String value of this part. */ - private String value; - - /** - * Constructor. - * - * @param name The name of the part - * @param value the string to post - * @param charset the charset to be used to encode the string, if <code>null</code> - * the {@link #DEFAULT_CHARSET default} is used - */ - public StringPart(String name, String value, String charset) { - - super( - name, - DEFAULT_CONTENT_TYPE, - charset == null ? DEFAULT_CHARSET : charset, - DEFAULT_TRANSFER_ENCODING - ); - if (value == null) { - throw new IllegalArgumentException("Value may not be null"); - } - if (value.indexOf(0) != -1) { - // See RFC 2048, 2.8. "8bit Data" - throw new IllegalArgumentException("NULs may not be present in string parts"); - } - this.value = value; - } - - /** - * Constructor. - * - * @param name The name of the part - * @param value the string to post - */ - public StringPart(String name, String value) { - this(name, value, null); - } - - /** - * Gets the content in bytes. Bytes are lazily created to allow the charset to be changed - * after the part is created. - * - * @return the content in bytes - */ - private byte[] getContent() { - if (content == null) { - content = EncodingUtils.getBytes(value, getCharSet()); - } - return content; - } - - /** - * Writes the data to the given OutputStream. - * @param out the OutputStream to write to - * @throws IOException if there is a write error - */ - @Override - protected void sendData(OutputStream out) throws IOException { - LOG.trace("enter sendData(OutputStream)"); - out.write(getContent()); - } - - /** - * Return the length of the data. - * @return The length of the data. - * @see Part#lengthOfData() - */ - @Override - protected long lengthOfData() { - LOG.trace("enter lengthOfData()"); - return getContent().length; - } - - /* (non-Javadoc) - * @see org.apache.commons.httpclient.methods.multipart.BasePart#setCharSet(java.lang.String) - */ - @Override - public void setCharSet(String charSet) { - super.setCharSet(charSet); - this.content = null; - } - -} diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 8107985..2477d94 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -355,7 +355,7 @@ public class ZygoteInit { Log.v(TAG, "Preloading resource #" + Integer.toHexString(id)); } if (id != 0) { - if (mResources.getColorStateList(id) == null) { + if (mResources.getColorStateList(id, null) == null) { throw new IllegalArgumentException( "Unable to find preloaded color resource #0x" + Integer.toHexString(id) diff --git a/core/java/com/android/internal/widget/ButtonBarLayout.java b/core/java/com/android/internal/widget/ButtonBarLayout.java new file mode 100644 index 0000000..64e6c69 --- /dev/null +++ b/core/java/com/android/internal/widget/ButtonBarLayout.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.View; +import android.widget.LinearLayout; + +import com.android.internal.R; + +/** + * An extension of LinearLayout that automatically switches to vertical + * orientation when it can't fit its child views horizontally. + */ +public class ButtonBarLayout extends LinearLayout { + /** Spacer used in horizontal orientation. */ + private final View mSpacer; + + /** Whether the current configuration allows stacking. */ + private final boolean mAllowStacked; + + /** Whether the layout is currently stacked. */ + private boolean mStacked; + + public ButtonBarLayout(Context context, AttributeSet attrs) { + super(context, attrs); + + mAllowStacked = context.getResources().getBoolean(R.bool.allow_stacked_button_bar); + mSpacer = findViewById(R.id.spacer); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + + // Maybe we can fit the content now? + if (w > oldw && mStacked) { + setStacked(false); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + if (mAllowStacked && getOrientation() == LinearLayout.HORIZONTAL) { + final int measuredWidth = getMeasuredWidthAndState(); + final int measuredWidthState = measuredWidth & MEASURED_STATE_MASK; + if (measuredWidthState == MEASURED_STATE_TOO_SMALL) { + setStacked(true); + + // Measure again in the new orientation. + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + } + } + + private void setStacked(boolean stacked) { + setOrientation(stacked ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL); + setGravity(stacked ? Gravity.RIGHT : Gravity.BOTTOM); + + if (mSpacer != null) { + mSpacer.setVisibility(stacked ? View.GONE : View.INVISIBLE); + } + + // Reverse the child order. This is specific to the Material button + // bar's layout XML and will probably not generalize. + final int childCount = getChildCount(); + for (int i = childCount - 2; i >= 0; i--) { + bringChildToFront(getChildAt(i)); + } + + mStacked = stacked; + } +} diff --git a/core/java/org/apache/http/conn/ConnectTimeoutException.java b/core/java/org/apache/http/conn/ConnectTimeoutException.java new file mode 100644 index 0000000..6cc6922 --- /dev/null +++ b/core/java/org/apache/http/conn/ConnectTimeoutException.java @@ -0,0 +1,69 @@ +/* + * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ConnectTimeoutException.java $ + * $Revision: 617645 $ + * $Date: 2008-02-01 13:05:31 -0800 (Fri, 01 Feb 2008) $ + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ + +package org.apache.http.conn; + +import java.io.InterruptedIOException; + +/** + * A timeout while connecting to an HTTP server or waiting for an + * available connection from an HttpConnectionManager. + * + * @author <a href="mailto:laura@lwerner.org">Laura Werner</a> + * + * @since 4.0 + * + * @deprecated Please use {@link java.net.URL#openConnection} instead. + * Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a> + * for further details. + */ +@Deprecated +public class ConnectTimeoutException extends InterruptedIOException { + + private static final long serialVersionUID = -4816682903149535989L; + + /** + * Creates a ConnectTimeoutException with a <tt>null</tt> detail message. + */ + public ConnectTimeoutException() { + super(); + } + + /** + * Creates a ConnectTimeoutException with the specified detail message. + * + * @param message The exception detail message + */ + public ConnectTimeoutException(final String message) { + super(message); + } + +} diff --git a/core/java/org/apache/http/conn/scheme/HostNameResolver.java b/core/java/org/apache/http/conn/scheme/HostNameResolver.java new file mode 100644 index 0000000..30ef298 --- /dev/null +++ b/core/java/org/apache/http/conn/scheme/HostNameResolver.java @@ -0,0 +1,47 @@ +/* + * $HeadURL:$ + * $Revision:$ + * $Date:$ + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ + +package org.apache.http.conn.scheme; + +import java.io.IOException; +import java.net.InetAddress; + +/** + * @deprecated Please use {@link java.net.URL#openConnection} instead. + * Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a> + * for further details. + */ +@Deprecated +public interface HostNameResolver { + + InetAddress resolve (String hostname) throws IOException; + +} diff --git a/core/java/org/apache/http/conn/scheme/LayeredSocketFactory.java b/core/java/org/apache/http/conn/scheme/LayeredSocketFactory.java new file mode 100644 index 0000000..b9f5348 --- /dev/null +++ b/core/java/org/apache/http/conn/scheme/LayeredSocketFactory.java @@ -0,0 +1,77 @@ +/* + * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/scheme/LayeredSocketFactory.java $ + * $Revision: 645850 $ + * $Date: 2008-04-08 04:08:52 -0700 (Tue, 08 Apr 2008) $ + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ + +package org.apache.http.conn.scheme; + +import java.io.IOException; +import java.net.Socket; +import java.net.UnknownHostException; + +/** + * A {@link SocketFactory SocketFactory} for layered sockets (SSL/TLS). + * See there for things to consider when implementing a socket factory. + * + * @author Michael Becke + * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a> + * @since 4.0 + * + * @deprecated Please use {@link java.net.URL#openConnection} instead. + * Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a> + * for further details. + */ +@Deprecated +public interface LayeredSocketFactory extends SocketFactory { + + /** + * Returns a socket connected to the given host that is layered over an + * existing socket. Used primarily for creating secure sockets through + * proxies. + * + * @param socket the existing socket + * @param host the host name/IP + * @param port the port on the host + * @param autoClose a flag for closing the underling socket when the created + * socket is closed + * + * @return Socket a new socket + * + * @throws IOException if an I/O error occurs while creating the socket + * @throws UnknownHostException if the IP address of the host cannot be + * determined + */ + Socket createSocket( + Socket socket, + String host, + int port, + boolean autoClose + ) throws IOException, UnknownHostException; + +} diff --git a/core/java/org/apache/http/conn/scheme/SocketFactory.java b/core/java/org/apache/http/conn/scheme/SocketFactory.java new file mode 100644 index 0000000..c6bc03c --- /dev/null +++ b/core/java/org/apache/http/conn/scheme/SocketFactory.java @@ -0,0 +1,143 @@ +/* + * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/scheme/SocketFactory.java $ + * $Revision: 645850 $ + * $Date: 2008-04-08 04:08:52 -0700 (Tue, 08 Apr 2008) $ + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ + +package org.apache.http.conn.scheme; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; + +import org.apache.http.conn.ConnectTimeoutException; +import org.apache.http.params.HttpParams; + +/** + * A factory for creating and connecting sockets. + * The factory encapsulates the logic for establishing a socket connection. + * <br/> + * Both {@link java.lang.Object#equals(java.lang.Object) Object.equals()} + * and {@link java.lang.Object#hashCode() Object.hashCode()} + * must be overridden for the correct operation of some connection managers. + * + * @author <a href="mailto:rolandw at apache.org">Roland Weber</a> + * @author Michael Becke + * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a> + * + * @deprecated Please use {@link java.net.URL#openConnection} instead. + * Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a> + * for further details. + */ +@Deprecated +public interface SocketFactory { + + /** + * Creates a new, unconnected socket. + * The socket should subsequently be passed to + * {@link #connectSocket connectSocket}. + * + * @return a new socket + * + * @throws IOException if an I/O error occurs while creating the socket + */ + Socket createSocket() + throws IOException + ; + + + /** + * Connects a socket to the given host. + * + * @param sock the socket to connect, as obtained from + * {@link #createSocket createSocket}. + * <code>null</code> indicates that a new socket + * should be created and connected. + * @param host the host to connect to + * @param port the port to connect to on the host + * @param localAddress the local address to bind the socket to, or + * <code>null</code> for any + * @param localPort the port on the local machine, + * 0 or a negative number for any + * @param params additional {@link HttpParams parameters} for connecting + * + * @return the connected socket. The returned object may be different + * from the <code>sock</code> argument if this factory supports + * a layered protocol. + * + * @throws IOException if an I/O error occurs + * @throws UnknownHostException if the IP address of the target host + * can not be determined + * @throws ConnectTimeoutException if the socket cannot be connected + * within the time limit defined in the <code>params</code> + */ + Socket connectSocket( + Socket sock, + String host, + int port, + InetAddress localAddress, + int localPort, + HttpParams params + ) throws IOException, UnknownHostException, ConnectTimeoutException; + + + /** + * Checks whether a socket provides a secure connection. + * The socket must be {@link #connectSocket connected} + * by this factory. + * The factory will <i>not</i> perform I/O operations + * in this method. + * <br/> + * As a rule of thumb, plain sockets are not secure and + * TLS/SSL sockets are secure. However, there may be + * application specific deviations. For example, a plain + * socket to a host in the same intranet ("trusted zone") + * could be considered secure. On the other hand, a + * TLS/SSL socket could be considered insecure based on + * the cypher suite chosen for the connection. + * + * @param sock the connected socket to check + * + * @return <code>true</code> if the connection of the socket + * should be considered secure, or + * <code>false</code> if it should not + * + * @throws IllegalArgumentException + * if the argument is invalid, for example because it is + * not a connected socket or was created by a different + * socket factory. + * Note that socket factories are <i>not</i> required to + * check these conditions, they may simply return a default + * value when called with an invalid socket argument. + */ + boolean isSecure(Socket sock) + throws IllegalArgumentException + ; + +} diff --git a/core/java/org/apache/http/conn/ssl/AbstractVerifier.java b/core/java/org/apache/http/conn/ssl/AbstractVerifier.java new file mode 100644 index 0000000..e264f1c --- /dev/null +++ b/core/java/org/apache/http/conn/ssl/AbstractVerifier.java @@ -0,0 +1,288 @@ +/* + * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/AbstractVerifier.java $ + * $Revision: 653041 $ + * $Date: 2008-05-03 03:39:28 -0700 (Sat, 03 May 2008) $ + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ + +package org.apache.http.conn.ssl; + +import java.util.regex.Pattern; + +import java.io.IOException; +import java.security.cert.Certificate; +import java.security.cert.CertificateParsingException; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.logging.Logger; +import java.util.logging.Level; + +import javax.net.ssl.DistinguishedNameParser; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; + +/** + * Abstract base class for all standard {@link X509HostnameVerifier} + * implementations. + * + * @author Julius Davies + * + * @deprecated Please use {@link java.net.URL#openConnection} instead. + * Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a> + * for further details. + */ +@Deprecated +public abstract class AbstractVerifier implements X509HostnameVerifier { + + private static final Pattern IPV4_PATTERN = Pattern.compile( + "^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$"); + + /** + * This contains a list of 2nd-level domains that aren't allowed to + * have wildcards when combined with country-codes. + * For example: [*.co.uk]. + * <p/> + * The [*.co.uk] problem is an interesting one. Should we just hope + * that CA's would never foolishly allow such a certificate to happen? + * Looks like we're the only implementation guarding against this. + * Firefox, Curl, Sun Java 1.4, 5, 6 don't bother with this check. + */ + private final static String[] BAD_COUNTRY_2LDS = + { "ac", "co", "com", "ed", "edu", "go", "gouv", "gov", "info", + "lg", "ne", "net", "or", "org" }; + + static { + // Just in case developer forgot to manually sort the array. :-) + Arrays.sort(BAD_COUNTRY_2LDS); + } + + public AbstractVerifier() { + super(); + } + + public final void verify(String host, SSLSocket ssl) + throws IOException { + if(host == null) { + throw new NullPointerException("host to verify is null"); + } + + SSLSession session = ssl.getSession(); + Certificate[] certs = session.getPeerCertificates(); + X509Certificate x509 = (X509Certificate) certs[0]; + verify(host, x509); + } + + public final boolean verify(String host, SSLSession session) { + try { + Certificate[] certs = session.getPeerCertificates(); + X509Certificate x509 = (X509Certificate) certs[0]; + verify(host, x509); + return true; + } + catch(SSLException e) { + return false; + } + } + + public final void verify(String host, X509Certificate cert) + throws SSLException { + String[] cns = getCNs(cert); + String[] subjectAlts = getDNSSubjectAlts(cert); + verify(host, cns, subjectAlts); + } + + public final void verify(final String host, final String[] cns, + final String[] subjectAlts, + final boolean strictWithSubDomains) + throws SSLException { + + // Build the list of names we're going to check. Our DEFAULT and + // STRICT implementations of the HostnameVerifier only use the + // first CN provided. All other CNs are ignored. + // (Firefox, wget, curl, Sun Java 1.4, 5, 6 all work this way). + LinkedList<String> names = new LinkedList<String>(); + if(cns != null && cns.length > 0 && cns[0] != null) { + names.add(cns[0]); + } + if(subjectAlts != null) { + for (String subjectAlt : subjectAlts) { + if (subjectAlt != null) { + names.add(subjectAlt); + } + } + } + + if(names.isEmpty()) { + String msg = "Certificate for <" + host + "> doesn't contain CN or DNS subjectAlt"; + throw new SSLException(msg); + } + + // StringBuffer for building the error message. + StringBuffer buf = new StringBuffer(); + + // We're can be case-insensitive when comparing the host we used to + // establish the socket to the hostname in the certificate. + String hostName = host.trim().toLowerCase(Locale.ENGLISH); + boolean match = false; + for(Iterator<String> it = names.iterator(); it.hasNext();) { + // Don't trim the CN, though! + String cn = it.next(); + cn = cn.toLowerCase(Locale.ENGLISH); + // Store CN in StringBuffer in case we need to report an error. + buf.append(" <"); + buf.append(cn); + buf.append('>'); + if(it.hasNext()) { + buf.append(" OR"); + } + + // The CN better have at least two dots if it wants wildcard + // action. It also can't be [*.co.uk] or [*.co.jp] or + // [*.org.uk], etc... + boolean doWildcard = cn.startsWith("*.") && + cn.indexOf('.', 2) != -1 && + acceptableCountryWildcard(cn) && + !isIPv4Address(host); + + if(doWildcard) { + match = hostName.endsWith(cn.substring(1)); + if(match && strictWithSubDomains) { + // If we're in strict mode, then [*.foo.com] is not + // allowed to match [a.b.foo.com] + match = countDots(hostName) == countDots(cn); + } + } else { + match = hostName.equals(cn); + } + if(match) { + break; + } + } + if(!match) { + throw new SSLException("hostname in certificate didn't match: <" + host + "> !=" + buf); + } + } + + public static boolean acceptableCountryWildcard(String cn) { + int cnLen = cn.length(); + if(cnLen >= 7 && cnLen <= 9) { + // Look for the '.' in the 3rd-last position: + if(cn.charAt(cnLen - 3) == '.') { + // Trim off the [*.] and the [.XX]. + String s = cn.substring(2, cnLen - 3); + // And test against the sorted array of bad 2lds: + int x = Arrays.binarySearch(BAD_COUNTRY_2LDS, s); + return x < 0; + } + } + return true; + } + + public static String[] getCNs(X509Certificate cert) { + DistinguishedNameParser dnParser = + new DistinguishedNameParser(cert.getSubjectX500Principal()); + List<String> cnList = dnParser.getAllMostSpecificFirst("cn"); + + if(!cnList.isEmpty()) { + String[] cns = new String[cnList.size()]; + cnList.toArray(cns); + return cns; + } else { + return null; + } + } + + + /** + * Extracts the array of SubjectAlt DNS names from an X509Certificate. + * Returns null if there aren't any. + * <p/> + * Note: Java doesn't appear able to extract international characters + * from the SubjectAlts. It can only extract international characters + * from the CN field. + * <p/> + * (Or maybe the version of OpenSSL I'm using to test isn't storing the + * international characters correctly in the SubjectAlts?). + * + * @param cert X509Certificate + * @return Array of SubjectALT DNS names stored in the certificate. + */ + public static String[] getDNSSubjectAlts(X509Certificate cert) { + LinkedList<String> subjectAltList = new LinkedList<String>(); + Collection<List<?>> c = null; + try { + c = cert.getSubjectAlternativeNames(); + } + catch(CertificateParsingException cpe) { + Logger.getLogger(AbstractVerifier.class.getName()) + .log(Level.FINE, "Error parsing certificate.", cpe); + } + if(c != null) { + for (List<?> aC : c) { + List<?> list = aC; + int type = ((Integer) list.get(0)).intValue(); + // If type is 2, then we've got a dNSName + if (type == 2) { + String s = (String) list.get(1); + subjectAltList.add(s); + } + } + } + if(!subjectAltList.isEmpty()) { + String[] subjectAlts = new String[subjectAltList.size()]; + subjectAltList.toArray(subjectAlts); + return subjectAlts; + } else { + return null; + } + } + + /** + * Counts the number of dots "." in a string. + * @param s string to count dots from + * @return number of dots + */ + public static int countDots(final String s) { + int count = 0; + for(int i = 0; i < s.length(); i++) { + if(s.charAt(i) == '.') { + count++; + } + } + return count; + } + + private static boolean isIPv4Address(final String input) { + return IPV4_PATTERN.matcher(input).matches(); + } +} diff --git a/core/java/org/apache/http/conn/ssl/AllowAllHostnameVerifier.java b/core/java/org/apache/http/conn/ssl/AllowAllHostnameVerifier.java new file mode 100644 index 0000000..c2bf4c4 --- /dev/null +++ b/core/java/org/apache/http/conn/ssl/AllowAllHostnameVerifier.java @@ -0,0 +1,59 @@ +/* + * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/AllowAllHostnameVerifier.java $ + * $Revision: 617642 $ + * $Date: 2008-02-01 12:54:07 -0800 (Fri, 01 Feb 2008) $ + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ + +package org.apache.http.conn.ssl; + +/** + * The ALLOW_ALL HostnameVerifier essentially turns hostname verification + * off. This implementation is a no-op, and never throws the SSLException. + * + * @author Julius Davies + * + * @deprecated Please use {@link java.net.URL#openConnection} instead. + * Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a> + * for further details. + */ +@Deprecated +public class AllowAllHostnameVerifier extends AbstractVerifier { + + public final void verify( + final String host, + final String[] cns, + final String[] subjectAlts) { + // Allow everything - so never blowup. + } + + @Override + public final String toString() { + return "ALLOW_ALL"; + } + +} diff --git a/core/java/org/apache/http/conn/ssl/BrowserCompatHostnameVerifier.java b/core/java/org/apache/http/conn/ssl/BrowserCompatHostnameVerifier.java new file mode 100644 index 0000000..48a7bf9 --- /dev/null +++ b/core/java/org/apache/http/conn/ssl/BrowserCompatHostnameVerifier.java @@ -0,0 +1,67 @@ +/* + * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/BrowserCompatHostnameVerifier.java $ + * $Revision: 617642 $ + * $Date: 2008-02-01 12:54:07 -0800 (Fri, 01 Feb 2008) $ + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ + +package org.apache.http.conn.ssl; + +import javax.net.ssl.SSLException; + +/** + * The HostnameVerifier that works the same way as Curl and Firefox. + * <p/> + * The hostname must match either the first CN, or any of the subject-alts. + * A wildcard can occur in the CN, and in any of the subject-alts. + * <p/> + * The only difference between BROWSER_COMPATIBLE and STRICT is that a wildcard + * (such as "*.foo.com") with BROWSER_COMPATIBLE matches all subdomains, + * including "a.b.foo.com". + * + * @author Julius Davies + * + * @deprecated Please use {@link java.net.URL#openConnection} instead. + * Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a> + * for further details. + */ +@Deprecated +public class BrowserCompatHostnameVerifier extends AbstractVerifier { + + public final void verify( + final String host, + final String[] cns, + final String[] subjectAlts) throws SSLException { + verify(host, cns, subjectAlts, false); + } + + @Override + public final String toString() { + return "BROWSER_COMPATIBLE"; + } + +} diff --git a/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java b/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java new file mode 100644 index 0000000..4d53d40 --- /dev/null +++ b/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java @@ -0,0 +1,408 @@ +/* + * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/SSLSocketFactory.java $ + * $Revision: 659194 $ + * $Date: 2008-05-22 11:33:47 -0700 (Thu, 22 May 2008) $ + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ + +package org.apache.http.conn.ssl; + +import org.apache.http.conn.scheme.HostNameResolver; +import org.apache.http.conn.scheme.LayeredSocketFactory; +import org.apache.http.params.HttpConnectionParams; +import org.apache.http.params.HttpParams; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.UnrecoverableKeyException; + +/** + * Layered socket factory for TLS/SSL connections, based on JSSE. + *. + * <p> + * SSLSocketFactory can be used to validate the identity of the HTTPS + * server against a list of trusted certificates and to authenticate to + * the HTTPS server using a private key. + * </p> + * + * <p> + * SSLSocketFactory will enable server authentication when supplied with + * a {@link KeyStore truststore} file containg one or several trusted + * certificates. The client secure socket will reject the connection during + * the SSL session handshake if the target HTTPS server attempts to + * authenticate itself with a non-trusted certificate. + * </p> + * + * <p> + * Use JDK keytool utility to import a trusted certificate and generate a truststore file: + * <pre> + * keytool -import -alias "my server cert" -file server.crt -keystore my.truststore + * </pre> + * </p> + * + * <p> + * SSLSocketFactory will enable client authentication when supplied with + * a {@link KeyStore keystore} file containg a private key/public certificate + * pair. The client secure socket will use the private key to authenticate + * itself to the target HTTPS server during the SSL session handshake if + * requested to do so by the server. + * The target HTTPS server will in its turn verify the certificate presented + * by the client in order to establish client's authenticity + * </p> + * + * <p> + * Use the following sequence of actions to generate a keystore file + * </p> + * <ul> + * <li> + * <p> + * Use JDK keytool utility to generate a new key + * <pre>keytool -genkey -v -alias "my client key" -validity 365 -keystore my.keystore</pre> + * For simplicity use the same password for the key as that of the keystore + * </p> + * </li> + * <li> + * <p> + * Issue a certificate signing request (CSR) + * <pre>keytool -certreq -alias "my client key" -file mycertreq.csr -keystore my.keystore</pre> + * </p> + * </li> + * <li> + * <p> + * Send the certificate request to the trusted Certificate Authority for signature. + * One may choose to act as her own CA and sign the certificate request using a PKI + * tool, such as OpenSSL. + * </p> + * </li> + * <li> + * <p> + * Import the trusted CA root certificate + * <pre>keytool -import -alias "my trusted ca" -file caroot.crt -keystore my.keystore</pre> + * </p> + * </li> + * <li> + * <p> + * Import the PKCS#7 file containg the complete certificate chain + * <pre>keytool -import -alias "my client key" -file mycert.p7 -keystore my.keystore</pre> + * </p> + * </li> + * <li> + * <p> + * Verify the content the resultant keystore file + * <pre>keytool -list -v -keystore my.keystore</pre> + * </p> + * </li> + * </ul> + * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a> + * @author Julius Davies + * + * @deprecated Please use {@link java.net.URL#openConnection} instead. + * Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a> + * for further details. + */ +@Deprecated +public class SSLSocketFactory implements LayeredSocketFactory { + + public static final String TLS = "TLS"; + public static final String SSL = "SSL"; + public static final String SSLV2 = "SSLv2"; + + public static final X509HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER + = new AllowAllHostnameVerifier(); + + public static final X509HostnameVerifier BROWSER_COMPATIBLE_HOSTNAME_VERIFIER + = new BrowserCompatHostnameVerifier(); + + public static final X509HostnameVerifier STRICT_HOSTNAME_VERIFIER + = new StrictHostnameVerifier(); + + /* + * Put defaults into holder class to avoid class preloading creating an + * instance of the classes referenced. + */ + private static class NoPreloadHolder { + /** + * The factory using the default JVM settings for secure connections. + */ + private static final SSLSocketFactory DEFAULT_FACTORY = new SSLSocketFactory(); + } + + /** + * Gets an singleton instance of the SSLProtocolSocketFactory. + * @return a SSLProtocolSocketFactory + */ + public static SSLSocketFactory getSocketFactory() { + return NoPreloadHolder.DEFAULT_FACTORY; + } + + private final SSLContext sslcontext; + private final javax.net.ssl.SSLSocketFactory socketfactory; + private final HostNameResolver nameResolver; + private X509HostnameVerifier hostnameVerifier = BROWSER_COMPATIBLE_HOSTNAME_VERIFIER; + + public SSLSocketFactory( + String algorithm, + final KeyStore keystore, + final String keystorePassword, + final KeyStore truststore, + final SecureRandom random, + final HostNameResolver nameResolver) + throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException + { + super(); + if (algorithm == null) { + algorithm = TLS; + } + KeyManager[] keymanagers = null; + if (keystore != null) { + keymanagers = createKeyManagers(keystore, keystorePassword); + } + TrustManager[] trustmanagers = null; + if (truststore != null) { + trustmanagers = createTrustManagers(truststore); + } + this.sslcontext = SSLContext.getInstance(algorithm); + this.sslcontext.init(keymanagers, trustmanagers, random); + this.socketfactory = this.sslcontext.getSocketFactory(); + this.nameResolver = nameResolver; + } + + public SSLSocketFactory( + final KeyStore keystore, + final String keystorePassword, + final KeyStore truststore) + throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException + { + this(TLS, keystore, keystorePassword, truststore, null, null); + } + + public SSLSocketFactory(final KeyStore keystore, final String keystorePassword) + throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException + { + this(TLS, keystore, keystorePassword, null, null, null); + } + + public SSLSocketFactory(final KeyStore truststore) + throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException + { + this(TLS, null, null, truststore, null, null); + } + + /** + * Constructs an HttpClient SSLSocketFactory backed by the given JSSE + * SSLSocketFactory. + * + * @hide + */ + public SSLSocketFactory(javax.net.ssl.SSLSocketFactory socketfactory) { + super(); + this.sslcontext = null; + this.socketfactory = socketfactory; + this.nameResolver = null; + } + + /** + * Creates the default SSL socket factory. + * This constructor is used exclusively to instantiate the factory for + * {@link #getSocketFactory getSocketFactory}. + */ + private SSLSocketFactory() { + super(); + this.sslcontext = null; + this.socketfactory = HttpsURLConnection.getDefaultSSLSocketFactory(); + this.nameResolver = null; + } + + private static KeyManager[] createKeyManagers(final KeyStore keystore, final String password) + throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { + if (keystore == null) { + throw new IllegalArgumentException("Keystore may not be null"); + } + KeyManagerFactory kmfactory = KeyManagerFactory.getInstance( + KeyManagerFactory.getDefaultAlgorithm()); + kmfactory.init(keystore, password != null ? password.toCharArray(): null); + return kmfactory.getKeyManagers(); + } + + private static TrustManager[] createTrustManagers(final KeyStore keystore) + throws KeyStoreException, NoSuchAlgorithmException { + if (keystore == null) { + throw new IllegalArgumentException("Keystore may not be null"); + } + TrustManagerFactory tmfactory = TrustManagerFactory.getInstance( + TrustManagerFactory.getDefaultAlgorithm()); + tmfactory.init(keystore); + return tmfactory.getTrustManagers(); + } + + + // non-javadoc, see interface org.apache.http.conn.SocketFactory + public Socket createSocket() + throws IOException { + + // the cast makes sure that the factory is working as expected + return (SSLSocket) this.socketfactory.createSocket(); + } + + + // non-javadoc, see interface org.apache.http.conn.SocketFactory + public Socket connectSocket( + final Socket sock, + final String host, + final int port, + final InetAddress localAddress, + int localPort, + final HttpParams params + ) throws IOException { + + if (host == null) { + throw new IllegalArgumentException("Target host may not be null."); + } + if (params == null) { + throw new IllegalArgumentException("Parameters may not be null."); + } + + SSLSocket sslsock = (SSLSocket) + ((sock != null) ? sock : createSocket()); + + if ((localAddress != null) || (localPort > 0)) { + + // we need to bind explicitly + if (localPort < 0) + localPort = 0; // indicates "any" + + InetSocketAddress isa = + new InetSocketAddress(localAddress, localPort); + sslsock.bind(isa); + } + + int connTimeout = HttpConnectionParams.getConnectionTimeout(params); + int soTimeout = HttpConnectionParams.getSoTimeout(params); + + InetSocketAddress remoteAddress; + if (this.nameResolver != null) { + remoteAddress = new InetSocketAddress(this.nameResolver.resolve(host), port); + } else { + remoteAddress = new InetSocketAddress(host, port); + } + + sslsock.connect(remoteAddress, connTimeout); + + sslsock.setSoTimeout(soTimeout); + try { + hostnameVerifier.verify(host, sslsock); + // verifyHostName() didn't blowup - good! + } catch (IOException iox) { + // close the socket before re-throwing the exception + try { sslsock.close(); } catch (Exception x) { /*ignore*/ } + throw iox; + } + + return sslsock; + } + + + /** + * Checks whether a socket connection is secure. + * This factory creates TLS/SSL socket connections + * which, by default, are considered secure. + * <br/> + * Derived classes may override this method to perform + * runtime checks, for example based on the cypher suite. + * + * @param sock the connected socket + * + * @return <code>true</code> + * + * @throws IllegalArgumentException if the argument is invalid + */ + public boolean isSecure(Socket sock) + throws IllegalArgumentException { + + if (sock == null) { + throw new IllegalArgumentException("Socket may not be null."); + } + // This instanceof check is in line with createSocket() above. + if (!(sock instanceof SSLSocket)) { + throw new IllegalArgumentException + ("Socket not created by this factory."); + } + // This check is performed last since it calls the argument object. + if (sock.isClosed()) { + throw new IllegalArgumentException("Socket is closed."); + } + + return true; + + } // isSecure + + + // non-javadoc, see interface LayeredSocketFactory + public Socket createSocket( + final Socket socket, + final String host, + final int port, + final boolean autoClose + ) throws IOException, UnknownHostException { + SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket( + socket, + host, + port, + autoClose + ); + hostnameVerifier.verify(host, sslSocket); + // verifyHostName() didn't blowup - good! + return sslSocket; + } + + public void setHostnameVerifier(X509HostnameVerifier hostnameVerifier) { + if ( hostnameVerifier == null ) { + throw new IllegalArgumentException("Hostname verifier may not be null"); + } + this.hostnameVerifier = hostnameVerifier; + } + + public X509HostnameVerifier getHostnameVerifier() { + return hostnameVerifier; + } + +} diff --git a/core/java/org/apache/http/conn/ssl/StrictHostnameVerifier.java b/core/java/org/apache/http/conn/ssl/StrictHostnameVerifier.java new file mode 100644 index 0000000..bd9e70d --- /dev/null +++ b/core/java/org/apache/http/conn/ssl/StrictHostnameVerifier.java @@ -0,0 +1,74 @@ +/* + * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/StrictHostnameVerifier.java $ + * $Revision: 617642 $ + * $Date: 2008-02-01 12:54:07 -0800 (Fri, 01 Feb 2008) $ + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ + +package org.apache.http.conn.ssl; + +import javax.net.ssl.SSLException; + +/** + * The Strict HostnameVerifier works the same way as Sun Java 1.4, Sun + * Java 5, Sun Java 6-rc. It's also pretty close to IE6. This + * implementation appears to be compliant with RFC 2818 for dealing with + * wildcards. + * <p/> + * The hostname must match either the first CN, or any of the subject-alts. + * A wildcard can occur in the CN, and in any of the subject-alts. The + * one divergence from IE6 is how we only check the first CN. IE6 allows + * a match against any of the CNs present. We decided to follow in + * Sun Java 1.4's footsteps and only check the first CN. (If you need + * to check all the CN's, feel free to write your own implementation!). + * <p/> + * A wildcard such as "*.foo.com" matches only subdomains in the same + * level, for example "a.foo.com". It does not match deeper subdomains + * such as "a.b.foo.com". + * + * @author Julius Davies + * + * @deprecated Please use {@link java.net.URL#openConnection} instead. + * Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a> + * for further details. + */ +@Deprecated +public class StrictHostnameVerifier extends AbstractVerifier { + + public final void verify( + final String host, + final String[] cns, + final String[] subjectAlts) throws SSLException { + verify(host, cns, subjectAlts, true); + } + + @Override + public final String toString() { + return "STRICT"; + } + +} diff --git a/core/java/org/apache/http/conn/ssl/X509HostnameVerifier.java b/core/java/org/apache/http/conn/ssl/X509HostnameVerifier.java new file mode 100644 index 0000000..e38db5f --- /dev/null +++ b/core/java/org/apache/http/conn/ssl/X509HostnameVerifier.java @@ -0,0 +1,91 @@ +/* + * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/X509HostnameVerifier.java $ + * $Revision: 618365 $ + * $Date: 2008-02-04 10:20:08 -0800 (Mon, 04 Feb 2008) $ + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ + +package org.apache.http.conn.ssl; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; +import java.io.IOException; +import java.security.cert.X509Certificate; + +/** + * Interface for checking if a hostname matches the names stored inside the + * server's X.509 certificate. Implements javax.net.ssl.HostnameVerifier, but + * we don't actually use that interface. Instead we added some methods that + * take String parameters (instead of javax.net.ssl.HostnameVerifier's + * SSLSession). JUnit is a lot easier this way! :-) + * <p/> + * We provide the HostnameVerifier.DEFAULT, HostnameVerifier.STRICT, and + * HostnameVerifier.ALLOW_ALL implementations. But feel free to define + * your own implementation! + * <p/> + * Inspired by Sebastian Hauer's original StrictSSLProtocolSocketFactory in the + * HttpClient "contrib" repository. + * + * @author Julius Davies + * @author <a href="mailto:hauer@psicode.com">Sebastian Hauer</a> + * + * @since 4.0 (8-Dec-2006) + * + * @deprecated Please use {@link java.net.URL#openConnection} instead. + * Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a> + * for further details. + */ +@Deprecated +public interface X509HostnameVerifier extends HostnameVerifier { + + boolean verify(String host, SSLSession session); + + void verify(String host, SSLSocket ssl) throws IOException; + + void verify(String host, X509Certificate cert) throws SSLException; + + /** + * Checks to see if the supplied hostname matches any of the supplied CNs + * or "DNS" Subject-Alts. Most implementations only look at the first CN, + * and ignore any additional CNs. Most implementations do look at all of + * the "DNS" Subject-Alts. The CNs or Subject-Alts may contain wildcards + * according to RFC 2818. + * + * @param cns CN fields, in order, as extracted from the X.509 + * certificate. + * @param subjectAlts Subject-Alt fields of type 2 ("DNS"), as extracted + * from the X.509 certificate. + * @param host The hostname to verify. + * @throws SSLException If verification failed. + */ + void verify(String host, String[] cns, String[] subjectAlts) + throws SSLException; + + +} diff --git a/core/java/org/apache/http/conn/ssl/package.html b/core/java/org/apache/http/conn/ssl/package.html new file mode 100644 index 0000000..a5c737f --- /dev/null +++ b/core/java/org/apache/http/conn/ssl/package.html @@ -0,0 +1,40 @@ +<html> +<head> +<!-- +/* + * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/package.html $ + * $Revision: 555193 $ + * $Date: 2007-07-11 00:36:47 -0700 (Wed, 11 Jul 2007) $ + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ +--> +</head> +<body> +TLS/SSL specific parts of the <i>HttpConn</i> API. + +</body> +</html> diff --git a/core/java/org/apache/http/params/CoreConnectionPNames.java b/core/java/org/apache/http/params/CoreConnectionPNames.java new file mode 100644 index 0000000..9479db1 --- /dev/null +++ b/core/java/org/apache/http/params/CoreConnectionPNames.java @@ -0,0 +1,136 @@ +/* + * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/CoreConnectionPNames.java $ + * $Revision: 576077 $ + * $Date: 2007-09-16 04:50:22 -0700 (Sun, 16 Sep 2007) $ + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ + +package org.apache.http.params; + + +/** + * Defines parameter names for connections in HttpCore. + * + * @version $Revision: 576077 $ + * + * @since 4.0 + * + * @deprecated Please use {@link java.net.URL#openConnection} instead. + * Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a> + * for further details. + */ +@Deprecated +public interface CoreConnectionPNames { + + /** + * Defines the default socket timeout (<tt>SO_TIMEOUT</tt>) in milliseconds which is the + * timeout for waiting for data. A timeout value of zero is interpreted as an infinite + * timeout. This value is used when no socket timeout is set in the + * method parameters. + * <p> + * This parameter expects a value of type {@link Integer}. + * </p> + * @see java.net.SocketOptions#SO_TIMEOUT + */ + public static final String SO_TIMEOUT = "http.socket.timeout"; + + /** + * Determines whether Nagle's algorithm is to be used. The Nagle's algorithm + * tries to conserve bandwidth by minimizing the number of segments that are + * sent. When applications wish to decrease network latency and increase + * performance, they can disable Nagle's algorithm (that is enable TCP_NODELAY). + * Data will be sent earlier, at the cost of an increase in bandwidth consumption. + * <p> + * This parameter expects a value of type {@link Boolean}. + * </p> + * @see java.net.SocketOptions#TCP_NODELAY + */ + public static final String TCP_NODELAY = "http.tcp.nodelay"; + + /** + * Determines the size of the internal socket buffer used to buffer data + * while receiving / transmitting HTTP messages. + * <p> + * This parameter expects a value of type {@link Integer}. + * </p> + */ + public static final String SOCKET_BUFFER_SIZE = "http.socket.buffer-size"; + + /** + * Sets SO_LINGER with the specified linger time in seconds. The maximum timeout + * value is platform specific. Value <tt>0</tt> implies that the option is disabled. + * Value <tt>-1</tt> implies that the JRE default is used. The setting only affects + * socket close. + * <p> + * This parameter expects a value of type {@link Integer}. + * </p> + * @see java.net.SocketOptions#SO_LINGER + */ + public static final String SO_LINGER = "http.socket.linger"; + + /** + * Determines the timeout until a connection is etablished. A value of zero + * means the timeout is not used. The default value is zero. + * <p> + * This parameter expects a value of type {@link Integer}. + * </p> + */ + public static final String CONNECTION_TIMEOUT = "http.connection.timeout"; + + /** + * Determines whether stale connection check is to be used. Disabling + * stale connection check may result in slight performance improvement + * at the risk of getting an I/O error when executing a request over a + * connection that has been closed at the server side. + * <p> + * This parameter expects a value of type {@link Boolean}. + * </p> + */ + public static final String STALE_CONNECTION_CHECK = "http.connection.stalecheck"; + + /** + * Determines the maximum line length limit. If set to a positive value, any HTTP + * line exceeding this limit will cause an IOException. A negative or zero value + * will effectively disable the check. + * <p> + * This parameter expects a value of type {@link Integer}. + * </p> + */ + public static final String MAX_LINE_LENGTH = "http.connection.max-line-length"; + + /** + * Determines the maximum HTTP header count allowed. If set to a positive value, + * the number of HTTP headers received from the data stream exceeding this limit + * will cause an IOException. A negative or zero value will effectively disable + * the check. + * <p> + * This parameter expects a value of type {@link Integer}. + * </p> + */ + public static final String MAX_HEADER_COUNT = "http.connection.max-header-count"; + +} diff --git a/core/java/org/apache/http/params/HttpConnectionParams.java b/core/java/org/apache/http/params/HttpConnectionParams.java new file mode 100644 index 0000000..a7b31fc --- /dev/null +++ b/core/java/org/apache/http/params/HttpConnectionParams.java @@ -0,0 +1,229 @@ +/* + * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/HttpConnectionParams.java $ + * $Revision: 576089 $ + * $Date: 2007-09-16 05:39:56 -0700 (Sun, 16 Sep 2007) $ + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ + +package org.apache.http.params; + +/** + * An adaptor for accessing connection parameters in {@link HttpParams}. + * <br/> + * Note that the <i>implements</i> relation to {@link CoreConnectionPNames} + * is for compatibility with existing application code only. References to + * the parameter names should use the interface, not this class. + * + * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a> + * + * @version $Revision: 576089 $ + * + * @since 4.0 + * + * @deprecated Please use {@link java.net.URL#openConnection} instead. + * Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a> + * for further details. + */ +@Deprecated +public final class HttpConnectionParams implements CoreConnectionPNames { + + /** + */ + private HttpConnectionParams() { + super(); + } + + /** + * Returns the default socket timeout (<tt>SO_TIMEOUT</tt>) in milliseconds which is the + * timeout for waiting for data. A timeout value of zero is interpreted as an infinite + * timeout. This value is used when no socket timeout is set in the + * method parameters. + * + * @return timeout in milliseconds + */ + public static int getSoTimeout(final HttpParams params) { + if (params == null) { + throw new IllegalArgumentException("HTTP parameters may not be null"); + } + return params.getIntParameter(CoreConnectionPNames.SO_TIMEOUT, 0); + } + + /** + * Sets the default socket timeout (<tt>SO_TIMEOUT</tt>) in milliseconds which is the + * timeout for waiting for data. A timeout value of zero is interpreted as an infinite + * timeout. This value is used when no socket timeout is set in the + * method parameters. + * + * @param timeout Timeout in milliseconds + */ + public static void setSoTimeout(final HttpParams params, int timeout) { + if (params == null) { + throw new IllegalArgumentException("HTTP parameters may not be null"); + } + params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, timeout); + + } + + /** + * Tests if Nagle's algorithm is to be used. + * + * @return <tt>true</tt> if the Nagle's algorithm is to NOT be used + * (that is enable TCP_NODELAY), <tt>false</tt> otherwise. + */ + public static boolean getTcpNoDelay(final HttpParams params) { + if (params == null) { + throw new IllegalArgumentException("HTTP parameters may not be null"); + } + return params.getBooleanParameter + (CoreConnectionPNames.TCP_NODELAY, true); + } + + /** + * Determines whether Nagle's algorithm is to be used. The Nagle's algorithm + * tries to conserve bandwidth by minimizing the number of segments that are + * sent. When applications wish to decrease network latency and increase + * performance, they can disable Nagle's algorithm (that is enable TCP_NODELAY). + * Data will be sent earlier, at the cost of an increase in bandwidth consumption. + * + * @param value <tt>true</tt> if the Nagle's algorithm is to NOT be used + * (that is enable TCP_NODELAY), <tt>false</tt> otherwise. + */ + public static void setTcpNoDelay(final HttpParams params, boolean value) { + if (params == null) { + throw new IllegalArgumentException("HTTP parameters may not be null"); + } + params.setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, value); + } + + public static int getSocketBufferSize(final HttpParams params) { + if (params == null) { + throw new IllegalArgumentException("HTTP parameters may not be null"); + } + return params.getIntParameter + (CoreConnectionPNames.SOCKET_BUFFER_SIZE, -1); + } + + public static void setSocketBufferSize(final HttpParams params, int size) { + if (params == null) { + throw new IllegalArgumentException("HTTP parameters may not be null"); + } + params.setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, size); + } + + /** + * Returns linger-on-close timeout. Value <tt>0</tt> implies that the option is + * disabled. Value <tt>-1</tt> implies that the JRE default is used. + * + * @return the linger-on-close timeout + */ + public static int getLinger(final HttpParams params) { + if (params == null) { + throw new IllegalArgumentException("HTTP parameters may not be null"); + } + return params.getIntParameter(CoreConnectionPNames.SO_LINGER, -1); + } + + /** + * Returns linger-on-close timeout. This option disables/enables immediate return + * from a close() of a TCP Socket. Enabling this option with a non-zero Integer + * timeout means that a close() will block pending the transmission and + * acknowledgement of all data written to the peer, at which point the socket is + * closed gracefully. Value <tt>0</tt> implies that the option is + * disabled. Value <tt>-1</tt> implies that the JRE default is used. + * + * @param value the linger-on-close timeout + */ + public static void setLinger(final HttpParams params, int value) { + if (params == null) { + throw new IllegalArgumentException("HTTP parameters may not be null"); + } + params.setIntParameter(CoreConnectionPNames.SO_LINGER, value); + } + + /** + * Returns the timeout until a connection is etablished. A value of zero + * means the timeout is not used. The default value is zero. + * + * @return timeout in milliseconds. + */ + public static int getConnectionTimeout(final HttpParams params) { + if (params == null) { + throw new IllegalArgumentException("HTTP parameters may not be null"); + } + return params.getIntParameter + (CoreConnectionPNames.CONNECTION_TIMEOUT, 0); + } + + /** + * Sets the timeout until a connection is etablished. A value of zero + * means the timeout is not used. The default value is zero. + * + * @param timeout Timeout in milliseconds. + */ + public static void setConnectionTimeout(final HttpParams params, int timeout) { + if (params == null) { + throw new IllegalArgumentException("HTTP parameters may not be null"); + } + params.setIntParameter + (CoreConnectionPNames.CONNECTION_TIMEOUT, timeout); + } + + /** + * Tests whether stale connection check is to be used. Disabling + * stale connection check may result in slight performance improvement + * at the risk of getting an I/O error when executing a request over a + * connection that has been closed at the server side. + * + * @return <tt>true</tt> if stale connection check is to be used, + * <tt>false</tt> otherwise. + */ + public static boolean isStaleCheckingEnabled(final HttpParams params) { + if (params == null) { + throw new IllegalArgumentException("HTTP parameters may not be null"); + } + return params.getBooleanParameter + (CoreConnectionPNames.STALE_CONNECTION_CHECK, true); + } + + /** + * Defines whether stale connection check is to be used. Disabling + * stale connection check may result in slight performance improvement + * at the risk of getting an I/O error when executing a request over a + * connection that has been closed at the server side. + * + * @param value <tt>true</tt> if stale connection check is to be used, + * <tt>false</tt> otherwise. + */ + public static void setStaleCheckingEnabled(final HttpParams params, boolean value) { + if (params == null) { + throw new IllegalArgumentException("HTTP parameters may not be null"); + } + params.setBooleanParameter + (CoreConnectionPNames.STALE_CONNECTION_CHECK, value); + } + +} diff --git a/core/java/org/apache/http/params/HttpParams.java b/core/java/org/apache/http/params/HttpParams.java new file mode 100644 index 0000000..9562e54 --- /dev/null +++ b/core/java/org/apache/http/params/HttpParams.java @@ -0,0 +1,192 @@ +/* + * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/params/HttpParams.java $ + * $Revision: 610763 $ + * $Date: 2008-01-10 04:01:13 -0800 (Thu, 10 Jan 2008) $ + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + * + */ + +package org.apache.http.params; + +/** + * Represents a collection of HTTP protocol and framework parameters. + * + * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a> + * + * @version $Revision: 610763 $ + * + * @since 4.0 + * + * @deprecated Please use {@link java.net.URL#openConnection} instead. + * Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a> + * for further details. + */ +@Deprecated +public interface HttpParams { + + /** + * Obtains the value of the given parameter. + * + * @param name the parent name. + * + * @return an object that represents the value of the parameter, + * <code>null</code> if the parameter is not set or if it + * is explicitly set to <code>null</code> + * + * @see #setParameter(String, Object) + */ + Object getParameter(String name); + + /** + * Assigns the value to the parameter with the given name. + * + * @param name parameter name + * @param value parameter value + */ + HttpParams setParameter(String name, Object value); + + /** + * Creates a copy of these parameters. + * + * @return a new set of parameters holding the same values as this one + */ + HttpParams copy(); + + /** + * Removes the parameter with the specified name. + * + * @param name parameter name + * + * @return true if the parameter existed and has been removed, false else. + */ + boolean removeParameter(String name); + + /** + * Returns a {@link Long} parameter value with the given name. + * If the parameter is not explicitly set, the default value is returned. + * + * @param name the parent name. + * @param defaultValue the default value. + * + * @return a {@link Long} that represents the value of the parameter. + * + * @see #setLongParameter(String, long) + */ + long getLongParameter(String name, long defaultValue); + + /** + * Assigns a {@link Long} to the parameter with the given name + * + * @param name parameter name + * @param value parameter value + */ + HttpParams setLongParameter(String name, long value); + + /** + * Returns an {@link Integer} parameter value with the given name. + * If the parameter is not explicitly set, the default value is returned. + * + * @param name the parent name. + * @param defaultValue the default value. + * + * @return a {@link Integer} that represents the value of the parameter. + * + * @see #setIntParameter(String, int) + */ + int getIntParameter(String name, int defaultValue); + + /** + * Assigns an {@link Integer} to the parameter with the given name + * + * @param name parameter name + * @param value parameter value + */ + HttpParams setIntParameter(String name, int value); + + /** + * Returns a {@link Double} parameter value with the given name. + * If the parameter is not explicitly set, the default value is returned. + * + * @param name the parent name. + * @param defaultValue the default value. + * + * @return a {@link Double} that represents the value of the parameter. + * + * @see #setDoubleParameter(String, double) + */ + double getDoubleParameter(String name, double defaultValue); + + /** + * Assigns a {@link Double} to the parameter with the given name + * + * @param name parameter name + * @param value parameter value + */ + HttpParams setDoubleParameter(String name, double value); + + /** + * Returns a {@link Boolean} parameter value with the given name. + * If the parameter is not explicitly set, the default value is returned. + * + * @param name the parent name. + * @param defaultValue the default value. + * + * @return a {@link Boolean} that represents the value of the parameter. + * + * @see #setBooleanParameter(String, boolean) + */ + boolean getBooleanParameter(String name, boolean defaultValue); + + /** + * Assigns a {@link Boolean} to the parameter with the given name + * + * @param name parameter name + * @param value parameter value + */ + HttpParams setBooleanParameter(String name, boolean value); + + /** + * Checks if a boolean parameter is set to <code>true</code>. + * + * @param name parameter name + * + * @return <tt>true</tt> if the parameter is set to value <tt>true</tt>, + * <tt>false</tt> if it is not set or set to <code>false</code> + */ + boolean isParameterTrue(String name); + + /** + * Checks if a boolean parameter is not set or <code>false</code>. + * + * @param name parameter name + * + * @return <tt>true</tt> if the parameter is either not set or + * set to value <tt>false</tt>, + * <tt>false</tt> if it is set to <code>true</code> + */ + boolean isParameterFalse(String name); + +} |