Move android-common to framework/ex

This is one necessary step by unbundling.

Change-Id: I9d922a52374ad6331fa2e39fa4b5e16ad7d108fa
This commit is contained in:
Ying Wang
2010-06-15 13:45:08 -07:00
parent 27357fc088
commit 6567adf8b6
15 changed files with 0 additions and 1780 deletions

View File

@@ -1,32 +0,0 @@
# Copyright (C) 2009 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.
LOCAL_PATH := $(call my-dir)
# Note: the source code is in java/, not src/, because this code is also part of
# the framework library, and build/core/pathmap.mk expects a java/ subdirectory.
include $(CLEAR_VARS)
LOCAL_MODULE := android-common
LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := \
$(call all-java-files-under, java) \
$(call all-logtags-files-under, java)
include $(BUILD_STATIC_JAVA_LIBRARY)
# Include this library in the build server's output directory
$(call dist-for-goals, droidcore, $(LOCAL_BUILT_MODULE):android-common.jar)
# Build the test package
include $(call all-makefiles-under, $(LOCAL_PATH))

View File

@@ -1,167 +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 com.android.common;
import android.database.AbstractCursor;
import android.database.CursorWindow;
import java.lang.System;
import java.util.ArrayList;
/**
* A convenience class that presents a two-dimensional ArrayList
* as a Cursor.
* @deprecated This is has been replaced by MatrixCursor.
*/
public class ArrayListCursor extends AbstractCursor {
private String[] mColumnNames;
private ArrayList<Object>[] mRows;
@SuppressWarnings({"unchecked"})
public ArrayListCursor(String[] columnNames, ArrayList<ArrayList> rows) {
int colCount = columnNames.length;
boolean foundID = false;
// Add an _id column if not in columnNames
for (int i = 0; i < colCount; ++i) {
if (columnNames[i].compareToIgnoreCase("_id") == 0) {
mColumnNames = columnNames;
foundID = true;
break;
}
}
if (!foundID) {
mColumnNames = new String[colCount + 1];
System.arraycopy(columnNames, 0, mColumnNames, 0, columnNames.length);
mColumnNames[colCount] = "_id";
}
int rowCount = rows.size();
mRows = new ArrayList[rowCount];
for (int i = 0; i < rowCount; ++i) {
mRows[i] = rows.get(i);
if (!foundID) {
mRows[i].add(i);
}
}
}
@Override
public void fillWindow(int position, CursorWindow window) {
if (position < 0 || position > getCount()) {
return;
}
window.acquireReference();
try {
int oldpos = mPos;
mPos = position - 1;
window.clear();
window.setStartPosition(position);
int columnNum = getColumnCount();
window.setNumColumns(columnNum);
while (moveToNext() && window.allocRow()) {
for (int i = 0; i < columnNum; i++) {
final Object data = mRows[mPos].get(i);
if (data != null) {
if (data instanceof byte[]) {
byte[] field = (byte[]) data;
if (!window.putBlob(field, mPos, i)) {
window.freeLastRow();
break;
}
} else {
String field = data.toString();
if (!window.putString(field, mPos, i)) {
window.freeLastRow();
break;
}
}
} else {
if (!window.putNull(mPos, i)) {
window.freeLastRow();
break;
}
}
}
}
mPos = oldpos;
} catch (IllegalStateException e){
// simply ignore it
} finally {
window.releaseReference();
}
}
@Override
public int getCount() {
return mRows.length;
}
@Override
public String[] getColumnNames() {
return mColumnNames;
}
@Override
public byte[] getBlob(int columnIndex) {
return (byte[]) mRows[mPos].get(columnIndex);
}
@Override
public String getString(int columnIndex) {
Object cell = mRows[mPos].get(columnIndex);
return (cell == null) ? null : cell.toString();
}
@Override
public short getShort(int columnIndex) {
Number num = (Number) mRows[mPos].get(columnIndex);
return num.shortValue();
}
@Override
public int getInt(int columnIndex) {
Number num = (Number) mRows[mPos].get(columnIndex);
return num.intValue();
}
@Override
public long getLong(int columnIndex) {
Number num = (Number) mRows[mPos].get(columnIndex);
return num.longValue();
}
@Override
public float getFloat(int columnIndex) {
Number num = (Number) mRows[mPos].get(columnIndex);
return num.floatValue();
}
@Override
public double getDouble(int columnIndex) {
Number num = (Number) mRows[mPos].get(columnIndex);
return num.doubleValue();
}
@Override
public boolean isNull(int columnIndex) {
return mRows[mPos].get(columnIndex) == null;
}
}

View File

@@ -1,100 +0,0 @@
# Copyright (C) 2010 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
option java_package com.android.common
#####
# This file contains definitions for event log (android.util.EventLog) tags
# used by Google Mobile Services applications. These definitions are part of
# the platform even when the Google applications are not.
#
# See system/core/logcat/event.logtags for a description of the file format.
#
# These event log tags must be assigned specific numbers (no "?") in the range
# 200000-300000. If new tags are added, be aware that older platforms will be
# missing the tag definitions, and may not be able to show them in their logs.
#####
# System Update (OTA)
# System update status bits
# [31- 9] Reserved for future use
# [ 8- 7] package verified (0=not attempted, 1=succeeded, 2=failed)
# [ 6] install approved
# [ 5] download approved
# [ 4- 0] status
201001 system_update (status|1|5),(download_result|1|5),(bytes|2|2),(url|3)
201002 system_update_user (action|3)
#####
# Android Market
# @param changes Number of changes made to database in reconstruct
202001 vending_reconstruct (changes|1)
#####
# Google Services Framework
203001 sync_details (authority|3),(send|1|2),(recv|1|2),(details|3)
203002 google_http_request (elapsed|2|3),(status|1),(appname|3),(reused|1)
#####
# Google Talk Service
# This event is logged when GTalkService encounters important events
204001 gtalkservice (eventType|1)
# This event is logged for GTalk connection state changes. The status field is an int, but
# it really contains 4 separate values, each taking up a byte
# (eventType << 24) + (connection state << 16) + (connection error << 8) + network state
204002 gtalk_connection (status|1)
# This event is logged when GTalk connection is closed.
# The status field is an int, but contains 2 different values, it's represented as
#
# (networkType << 8) + connection error
#
# the possible error values are
#
# no_error=0, no_network=1, connection_failed=2, unknown_host=3, auth_failed=4,
# auth_expired=5, heart_beat_timeout=6, server_error=7, server_reject_rate_limiting=8, unknown=10
#
# duration is the connection duration.
204003 gtalk_conn_close (status|1),(duration|1)
# This event is logged for GTalk heartbeat resets
# interval_and_nt contains both the heartbeat interval and the network type, It's represented as
# (networkType << 16) + interval
# interval is in seconds; network type can be 0 (mobile) or 1 (wifi); ip is the host ip addr.
204004 gtalk_heartbeat_reset (interval_and_nt|1),(ip|3)
# This event is logged when an Rmq v2 packet is sent or received.
204005 c2dm (packet_type|1),(persistent_id|3),(stream_id|1),(last_stream_id|1)
#####
# Google Login Service and Setup Wizard
# This event is for when communicating to the server times out during account setup
205001 setup_server_timeout
205002 setup_required_captcha (action|3)
205003 setup_io_error (status|3)
205004 setup_server_error
205005 setup_retries_exhausted
205006 setup_no_data_network
205007 setup_completed
205008 gls_account_tried (status|1)
205009 gls_account_saved (status|1)
205010 gls_authenticate (status|1),(service|3)
205011 google_mail_switch (direction|1)

View File

@@ -1,224 +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 com.android.common;
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.os.Handler;
import android.os.Message;
import android.util.Log;
import java.util.HashMap;
import java.util.Iterator;
/**
* A wrapper for a broadcast receiver that provides network connectivity
* state information, independent of network type (mobile, Wi-Fi, etc.).
* @deprecated Code tempted to use this class should simply listen for connectivity intents
* (or poll ConnectivityManager) directly.
* {@hide}
*/
public class NetworkConnectivityListener {
private static final String TAG = "NetworkConnectivityListener";
private static final boolean DBG = false;
private Context mContext;
private HashMap<Handler, Integer> mHandlers = new HashMap<Handler, Integer>();
private State mState;
private boolean mListening;
private String mReason;
private boolean mIsFailover;
/** Network connectivity information */
private NetworkInfo mNetworkInfo;
/**
* In case of a Disconnect, the connectivity manager may have
* already established, or may be attempting to establish, connectivity
* with another network. If so, {@code mOtherNetworkInfo} will be non-null.
*/
private NetworkInfo mOtherNetworkInfo;
private ConnectivityBroadcastReceiver mReceiver;
private class ConnectivityBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (!action.equals(ConnectivityManager.CONNECTIVITY_ACTION) ||
mListening == false) {
Log.w(TAG, "onReceived() called with " + mState.toString() + " and " + intent);
return;
}
boolean noConnectivity =
intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
if (noConnectivity) {
mState = State.NOT_CONNECTED;
} else {
mState = State.CONNECTED;
}
mNetworkInfo = (NetworkInfo)
intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
mOtherNetworkInfo = (NetworkInfo)
intent.getParcelableExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO);
mReason = intent.getStringExtra(ConnectivityManager.EXTRA_REASON);
mIsFailover =
intent.getBooleanExtra(ConnectivityManager.EXTRA_IS_FAILOVER, false);
if (DBG) {
Log.d(TAG, "onReceive(): mNetworkInfo=" + mNetworkInfo + " mOtherNetworkInfo = "
+ (mOtherNetworkInfo == null ? "[none]" : mOtherNetworkInfo +
" noConn=" + noConnectivity) + " mState=" + mState.toString());
}
// Notifiy any handlers.
Iterator<Handler> it = mHandlers.keySet().iterator();
while (it.hasNext()) {
Handler target = it.next();
Message message = Message.obtain(target, mHandlers.get(target));
target.sendMessage(message);
}
}
};
public enum State {
UNKNOWN,
/** This state is returned if there is connectivity to any network **/
CONNECTED,
/**
* This state is returned if there is no connectivity to any network. This is set
* to true under two circumstances:
* <ul>
* <li>When connectivity is lost to one network, and there is no other available
* network to attempt to switch to.</li>
* <li>When connectivity is lost to one network, and the attempt to switch to
* another network fails.</li>
*/
NOT_CONNECTED
}
/**
* Create a new NetworkConnectivityListener.
*/
public NetworkConnectivityListener() {
mState = State.UNKNOWN;
mReceiver = new ConnectivityBroadcastReceiver();
}
/**
* This method starts listening for network connectivity state changes.
* @param context
*/
public synchronized void startListening(Context context) {
if (!mListening) {
mContext = context;
IntentFilter filter = new IntentFilter();
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
context.registerReceiver(mReceiver, filter);
mListening = true;
}
}
/**
* This method stops this class from listening for network changes.
*/
public synchronized void stopListening() {
if (mListening) {
mContext.unregisterReceiver(mReceiver);
mContext = null;
mNetworkInfo = null;
mOtherNetworkInfo = null;
mIsFailover = false;
mReason = null;
mListening = false;
}
}
/**
* This methods registers a Handler to be called back onto with the specified what code when
* the network connectivity state changes.
*
* @param target The target handler.
* @param what The what code to be used when posting a message to the handler.
*/
public void registerHandler(Handler target, int what) {
mHandlers.put(target, what);
}
/**
* This methods unregisters the specified Handler.
* @param target
*/
public void unregisterHandler(Handler target) {
mHandlers.remove(target);
}
public State getState() {
return mState;
}
/**
* Return the NetworkInfo associated with the most recent connectivity event.
* @return {@code NetworkInfo} for the network that had the most recent connectivity event.
*/
public NetworkInfo getNetworkInfo() {
return mNetworkInfo;
}
/**
* If the most recent connectivity event was a DISCONNECT, return
* any information supplied in the broadcast about an alternate
* network that might be available. If this returns a non-null
* value, then another broadcast should follow shortly indicating
* whether connection to the other network succeeded.
*
* @return NetworkInfo
*/
public NetworkInfo getOtherNetworkInfo() {
return mOtherNetworkInfo;
}
/**
* Returns true if the most recent event was for an attempt to switch over to
* a new network following loss of connectivity on another network.
* @return {@code true} if this was a failover attempt, {@code false} otherwise.
*/
public boolean isFailover() {
return mIsFailover;
}
/**
* An optional reason for the connectivity state change may have been supplied.
* This returns it.
* @return the reason for the state change, if available, or {@code null}
* otherwise.
*/
public String getReason() {
return mReason;
}
}

View File

@@ -1,348 +0,0 @@
/*
* Copyright (C) 2009 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.common;
import android.content.SharedPreferences;
import android.net.http.AndroidHttpClient;
import android.text.format.Time;
import java.util.Map;
import java.util.TreeSet;
/**
* Tracks the success/failure history of a particular network operation in
* persistent storage and computes retry strategy accordingly. Handles
* exponential backoff, periodic rescheduling, event-driven triggering,
* retry-after moratorium intervals, etc. based on caller-specified parameters.
*
* <p>This class does not directly perform or invoke any operations,
* it only keeps track of the schedule. Somebody else needs to call
* {@link #getNextTimeMillis()} as appropriate and do the actual work.
*/
public class OperationScheduler {
/** Tunable parameter options for {@link #getNextTimeMillis}. */
public static class Options {
/** Wait this long after every error before retrying. */
public long backoffFixedMillis = 0;
/** Wait this long times the number of consecutive errors so far before retrying. */
public long backoffIncrementalMillis = 5000;
/** Maximum duration of moratorium to honor. Mostly an issue for clock rollbacks. */
public long maxMoratoriumMillis = 24 * 3600 * 1000;
/** Minimum duration after success to wait before allowing another trigger. */
public long minTriggerMillis = 0;
/** Automatically trigger this long after the last success. */
public long periodicIntervalMillis = 0;
@Override
public String toString() {
return String.format(
"OperationScheduler.Options[backoff=%.1f+%.1f max=%.1f min=%.1f period=%.1f]",
backoffFixedMillis / 1000.0, backoffIncrementalMillis / 1000.0,
maxMoratoriumMillis / 1000.0, minTriggerMillis / 1000.0,
periodicIntervalMillis / 1000.0);
}
}
private static final String PREFIX = "OperationScheduler_";
private final SharedPreferences mStorage;
/**
* Initialize the scheduler state.
* @param storage to use for recording the state of operations across restarts/reboots
*/
public OperationScheduler(SharedPreferences storage) {
mStorage = storage;
}
/**
* Parse scheduler options supplied in this string form:
*
* <pre>
* backoff=(fixed)+(incremental) max=(maxmoratorium) min=(mintrigger) [period=](interval)
* </pre>
*
* All values are times in (possibly fractional) <em>seconds</em> (not milliseconds).
* Omitted settings are left at whatever existing default value was passed in.
*
* <p>
* The default options: <code>backoff=0+5 max=86400 min=0 period=0</code><br>
* Fractions are OK: <code>backoff=+2.5 period=10.0</code><br>
* The "period=" can be omitted: <code>3600</code><br>
*
* @param spec describing some or all scheduler options.
* @param options to update with parsed values.
* @return the options passed in (for convenience)
* @throws IllegalArgumentException if the syntax is invalid
*/
public static Options parseOptions(String spec, Options options)
throws IllegalArgumentException {
for (String param : spec.split(" +")) {
if (param.length() == 0) continue;
if (param.startsWith("backoff=")) {
int plus = param.indexOf('+', 8);
if (plus < 0) {
options.backoffFixedMillis = parseSeconds(param.substring(8));
} else {
if (plus > 8) {
options.backoffFixedMillis = parseSeconds(param.substring(8, plus));
}
options.backoffIncrementalMillis = parseSeconds(param.substring(plus + 1));
}
} else if (param.startsWith("max=")) {
options.maxMoratoriumMillis = parseSeconds(param.substring(4));
} else if (param.startsWith("min=")) {
options.minTriggerMillis = parseSeconds(param.substring(4));
} else if (param.startsWith("period=")) {
options.periodicIntervalMillis = parseSeconds(param.substring(7));
} else {
options.periodicIntervalMillis = parseSeconds(param);
}
}
return options;
}
private static long parseSeconds(String param) throws NumberFormatException {
return (long) (Float.parseFloat(param) * 1000);
}
/**
* Compute the time of the next operation. Does not modify any state
* (unless the clock rolls backwards, in which case timers are reset).
*
* @param options to use for this computation.
* @return the wall clock time ({@link System#currentTimeMillis()}) when the
* next operation should be attempted -- immediately, if the return value is
* before the current time.
*/
public long getNextTimeMillis(Options options) {
boolean enabledState = mStorage.getBoolean(PREFIX + "enabledState", true);
if (!enabledState) return Long.MAX_VALUE;
boolean permanentError = mStorage.getBoolean(PREFIX + "permanentError", false);
if (permanentError) return Long.MAX_VALUE;
// We do quite a bit of limiting to prevent a clock rollback from totally
// hosing the scheduler. Times which are supposed to be in the past are
// clipped to the current time so we don't languish forever.
int errorCount = mStorage.getInt(PREFIX + "errorCount", 0);
long now = currentTimeMillis();
long lastSuccessTimeMillis = getTimeBefore(PREFIX + "lastSuccessTimeMillis", now);
long lastErrorTimeMillis = getTimeBefore(PREFIX + "lastErrorTimeMillis", now);
long triggerTimeMillis = mStorage.getLong(PREFIX + "triggerTimeMillis", Long.MAX_VALUE);
long moratoriumSetMillis = getTimeBefore(PREFIX + "moratoriumSetTimeMillis", now);
long moratoriumTimeMillis = getTimeBefore(PREFIX + "moratoriumTimeMillis",
moratoriumSetMillis + options.maxMoratoriumMillis);
long time = triggerTimeMillis;
if (options.periodicIntervalMillis > 0) {
time = Math.min(time, lastSuccessTimeMillis + options.periodicIntervalMillis);
}
time = Math.max(time, moratoriumTimeMillis);
time = Math.max(time, lastSuccessTimeMillis + options.minTriggerMillis);
if (errorCount > 0) {
time = Math.max(time, lastErrorTimeMillis + options.backoffFixedMillis +
options.backoffIncrementalMillis * errorCount);
}
return time;
}
/**
* Return the last time the operation completed. Does not modify any state.
*
* @return the wall clock time when {@link #onSuccess()} was last called.
*/
public long getLastSuccessTimeMillis() {
return mStorage.getLong(PREFIX + "lastSuccessTimeMillis", 0);
}
/**
* Return the last time the operation was attempted. Does not modify any state.
*
* @return the wall clock time when {@link #onSuccess()} or {@link
* #onTransientError()} was last called.
*/
public long getLastAttemptTimeMillis() {
return Math.max(
mStorage.getLong(PREFIX + "lastSuccessTimeMillis", 0),
mStorage.getLong(PREFIX + "lastErrorTimeMillis", 0));
}
/**
* Fetch a {@link SharedPreferences} property, but force it to be before
* a certain time, updating the value if necessary. This is to recover
* gracefully from clock rollbacks which could otherwise strand our timers.
*
* @param name of SharedPreferences key
* @param max time to allow in result
* @return current value attached to key (default 0), limited by max
*/
private long getTimeBefore(String name, long max) {
long time = mStorage.getLong(name, 0);
if (time > max) mStorage.edit().putLong(name, (time = max)).commit();
return time;
}
/**
* Request an operation to be performed at a certain time. The actual
* scheduled time may be affected by error backoff logic and defined
* minimum intervals. Use {@link Long#MAX_VALUE} to disable triggering.
*
* @param millis wall clock time ({@link System#currentTimeMillis()}) to
* trigger another operation; 0 to trigger immediately
*/
public void setTriggerTimeMillis(long millis) {
mStorage.edit().putLong(PREFIX + "triggerTimeMillis", millis).commit();
}
/**
* Forbid any operations until after a certain (absolute) time.
* Limited by {@link #Options.maxMoratoriumMillis}.
*
* @param millis wall clock time ({@link System#currentTimeMillis()})
* when operations should be allowed again; 0 to remove moratorium
*/
public void setMoratoriumTimeMillis(long millis) {
mStorage.edit()
.putLong(PREFIX + "moratoriumTimeMillis", millis)
.putLong(PREFIX + "moratoriumSetTimeMillis", currentTimeMillis())
.commit();
}
/**
* Forbid any operations until after a certain time, as specified in
* the format used by the HTTP "Retry-After" header.
* Limited by {@link #Options.maxMoratoriumMillis}.
*
* @param retryAfter moratorium time in HTTP format
* @return true if a time was successfully parsed
*/
public boolean setMoratoriumTimeHttp(String retryAfter) {
try {
long ms = Long.valueOf(retryAfter) * 1000;
setMoratoriumTimeMillis(ms + currentTimeMillis());
return true;
} catch (NumberFormatException nfe) {
try {
setMoratoriumTimeMillis(AndroidHttpClient.parseDate(retryAfter));
return true;
} catch (IllegalArgumentException iae) {
return false;
}
}
}
/**
* Enable or disable all operations. When disabled, all calls to
* {@link #getNextTimeMillis()} return {@link Long#MAX_VALUE}.
* Commonly used when data network availability goes up and down.
*
* @param enabled if operations can be performed
*/
public void setEnabledState(boolean enabled) {
mStorage.edit().putBoolean(PREFIX + "enabledState", enabled).commit();
}
/**
* Report successful completion of an operation. Resets all error
* counters, clears any trigger directives, and records the success.
*/
public void onSuccess() {
resetTransientError();
resetPermanentError();
mStorage.edit()
.remove(PREFIX + "errorCount")
.remove(PREFIX + "lastErrorTimeMillis")
.remove(PREFIX + "permanentError")
.remove(PREFIX + "triggerTimeMillis")
.putLong(PREFIX + "lastSuccessTimeMillis", currentTimeMillis()).commit();
}
/**
* Report a transient error (usually a network failure). Increments
* the error count and records the time of the latest error for backoff
* purposes.
*/
public void onTransientError() {
mStorage.edit().putLong(PREFIX + "lastErrorTimeMillis", currentTimeMillis()).commit();
mStorage.edit().putInt(PREFIX + "errorCount",
mStorage.getInt(PREFIX + "errorCount", 0) + 1).commit();
}
/**
* Reset all transient error counts, allowing the next operation to proceed
* immediately without backoff. Commonly used on network state changes, when
* partial progress occurs (some data received), and in other circumstances
* where there is reason to hope things might start working better.
*/
public void resetTransientError() {
mStorage.edit().remove(PREFIX + "errorCount").commit();
}
/**
* Report a permanent error that will not go away until further notice.
* No operation will be scheduled until {@link #resetPermanentError()}
* is called. Commonly used for authentication failures (which are reset
* when the accounts database is updated).
*/
public void onPermanentError() {
mStorage.edit().putBoolean(PREFIX + "permanentError", true).commit();
}
/**
* Reset any permanent error status set by {@link #onPermanentError},
* allowing operations to be scheduled as normal.
*/
public void resetPermanentError() {
mStorage.edit().remove(PREFIX + "permanentError").commit();
}
/**
* Return a string description of the scheduler state for debugging.
*/
public String toString() {
StringBuilder out = new StringBuilder("[OperationScheduler:");
for (String key : new TreeSet<String>(mStorage.getAll().keySet())) { // Sort keys
if (key.startsWith(PREFIX)) {
if (key.endsWith("TimeMillis")) {
Time time = new Time();
time.set(mStorage.getLong(key, 0));
out.append(" ").append(key.substring(PREFIX.length(), key.length() - 10));
out.append("=").append(time.format("%Y-%m-%d/%H:%M:%S"));
} else {
out.append(" ").append(key.substring(PREFIX.length()));
out.append("=").append(mStorage.getAll().get(key).toString());
}
}
}
return out.append("]").toString();
}
/**
* Gets the current time. Can be overridden for unit testing.
*
* @return {@link System#currentTimeMillis()}
*/
protected long currentTimeMillis() {
return System.currentTimeMillis();
}
}

View File

@@ -1,74 +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 com.android.common;
import android.text.InputFilter;
import android.text.Spanned;
import android.text.SpannableStringBuilder;
/**
* Implements special address cleanup rules:
* The first space key entry following an "@" symbol that is followed by any combination
* of letters and symbols, including one+ dots and zero commas, should insert an extra
* comma (followed by the space).
*
* @hide
*/
public class Rfc822InputFilter implements InputFilter {
public CharSequence filter(CharSequence source, int start, int end, Spanned dest,
int dstart, int dend) {
// quick check - did they enter a single space?
if (end-start != 1 || source.charAt(start) != ' ') {
return null;
}
// determine if the characters before the new space fit the pattern
// follow backwards and see if we find a comma, dot, or @
int scanBack = dstart;
boolean dotFound = false;
while (scanBack > 0) {
char c = dest.charAt(--scanBack);
switch (c) {
case '.':
dotFound = true; // one or more dots are req'd
break;
case ',':
return null;
case '@':
if (!dotFound) {
return null;
}
// we have found a comma-insert case. now just do it
// in the least expensive way we can.
if (source instanceof Spanned) {
SpannableStringBuilder sb = new SpannableStringBuilder(",");
sb.append(source);
return sb;
} else {
return ", ";
}
default:
// just keep going
}
}
// no termination cases were found, so don't edit the input
return null;
}
}

View File

@@ -1,134 +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 com.android.common;
import android.text.TextUtils;
import android.text.util.Rfc822Token;
import android.text.util.Rfc822Tokenizer;
import android.widget.AutoCompleteTextView;
import java.util.regex.Pattern;
/**
* This class works as a Validator for AutoCompleteTextView for
* email addresses. If a token does not appear to be a valid address,
* it is trimmed of characters that cannot legitimately appear in one
* and has the specified domain name added. It is meant for use with
* {@link Rfc822Token} and {@link Rfc822Tokenizer}.
*
* @deprecated In the future make sure we don't quietly alter the user's
* text in ways they did not intend. Meanwhile, hide this
* class from the public API because it does not even have
* a full understanding of the syntax it claims to correct.
* @hide
*/
public class Rfc822Validator implements AutoCompleteTextView.Validator {
/*
* Regex.EMAIL_ADDRESS_PATTERN hardcodes the TLD that we accept, but we
* want to make sure we will keep accepting email addresses with TLD's
* that don't exist at the time of this writing, so this regexp relaxes
* that constraint by accepting any kind of top level domain, not just
* ".com", ".fr", etc...
*/
private static final Pattern EMAIL_ADDRESS_PATTERN =
Pattern.compile("[^\\s@]+@[^\\s@]+\\.[a-zA-z][a-zA-Z][a-zA-Z]*");
private String mDomain;
/**
* Constructs a new validator that uses the specified domain name as
* the default when none is specified.
*/
public Rfc822Validator(String domain) {
mDomain = domain;
}
/**
* {@inheritDoc}
*/
public boolean isValid(CharSequence text) {
Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(text);
return tokens.length == 1 &&
EMAIL_ADDRESS_PATTERN.
matcher(tokens[0].getAddress()).matches();
}
/**
* @return a string in which all the characters that are illegal for the username
* or the domain name part of the email address have been removed.
*/
private String removeIllegalCharacters(String s) {
StringBuilder result = new StringBuilder();
int length = s.length();
for (int i = 0; i < length; i++) {
char c = s.charAt(i);
/*
* An RFC822 atom can contain any ASCII printing character
* except for periods and any of the following punctuation.
* A local-part can contain multiple atoms, concatenated by
* periods, so do allow periods here.
*/
if (c <= ' ' || c > '~') {
continue;
}
if (c == '(' || c == ')' || c == '<' || c == '>' ||
c == '@' || c == ',' || c == ';' || c == ':' ||
c == '\\' || c == '"' || c == '[' || c == ']') {
continue;
}
result.append(c);
}
return result.toString();
}
/**
* {@inheritDoc}
*/
public CharSequence fixText(CharSequence cs) {
// Return an empty string if the email address only contains spaces, \n or \t
if (TextUtils.getTrimmedLength(cs) == 0) return "";
Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(cs);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < tokens.length; i++) {
String text = tokens[i].getAddress();
int index = text.indexOf('@');
if (index < 0) {
// If there is no @, just append the domain of the account
tokens[i].setAddress(removeIllegalCharacters(text) + "@" + mDomain);
} else {
// Otherwise, remove the illegal characters on both sides of the '@'
String fix = removeIllegalCharacters(text.substring(0, index));
String domain = removeIllegalCharacters(text.substring(index + 1));
tokens[i].setAddress(fix + "@" + (domain.length() != 0 ? domain : mDomain));
}
sb.append(tokens[i].toString());
if (i + 1 < tokens.length) {
sb.append(", ");
}
}
return sb;
}
}

View File

@@ -1,38 +0,0 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.common;
/**
* Utilities for search implementations.
*
* @see android.app.SearchManager
*/
public class Search {
/**
* Key for the source identifier set by the application that launched a search intent.
* The identifier is search-source specific string. It can be used
* by the search provider to keep statistics of where searches are started from.
*
* The source identifier is stored in the {@link android.app.SearchManager#APP_DATA}
* Bundle in {@link android.content.Intent#ACTION_SEARCH} and
* {@link android.content.Intent#ACTION_WEB_SEARCH} intents.
*/
public final static String SOURCE = "source";
private Search() { } // don't instantiate
}

View File

@@ -1,136 +0,0 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.common.speech;
/**
* Logging event constants used for Voice Search and VoiceIME. These are the
* keys and values of extras to be specified in logging broadcast intents.
* This class is used by clients of the android.speech APIs to log how the
* user interacts with the IME settings and speech recognition result.
*/
public class LoggingEvents {
// The name of the broadcast intent for logging.
public static final String ACTION_LOG_EVENT = "com.android.common.speech.LOG_EVENT";
// The extra key used for the name of the app being logged.
public static final String EXTRA_APP_NAME = "app_name";
// The extra key used for the name of the app issuing the VoiceSearch
// or VoiceIME request
public static final String EXTRA_CALLING_APP_NAME = "";
// The extra key used for the event value. The possible event values depend
// on the app being logged for, and are defined in the subclasses below.
public static final String EXTRA_EVENT = "extra_event";
// The extra key used to log the time in milliseconds at which the EXTRA_EVENT
// occurred in the client.
public static final String EXTRA_TIMESTAMP = "timestamp";
// The extra key used (with a boolean value of 'true') as a way to trigger a
// flush of the log events to the server.
public static final String EXTRA_FLUSH = "flush";
/**
* Logging event constants for voice search. Below are the extra values for
* {@link LoggingEvents#EXTRA_EVENT}, clustered with keys to additional
* extras for some events that need to be included as additional fields in
* the event. Note that this is not representative of *all* voice search
* events - only the ones that need to be reported from outside the voice
* search app, such as from Browser.
*/
public class VoiceSearch {
// The app name to be used for logging VoiceSearch events.
public static final String APP_NAME = "googlemobile";
public static final int RETRY = 0;
public static final int N_BEST_REVEAL = 1;
public static final int N_BEST_CHOOSE = 2;
public static final String EXTRA_N_BEST_CHOOSE_INDEX = "index"; // value should be int
public static final int QUERY_UPDATED = 3;
public static final String EXTRA_QUERY_UPDATED_VALUE = "value"; // value should be String
}
/**
* Logging event constants for VoiceIME. Below are the extra values for
* {@link LoggingEvents#EXTRA_EVENT}, clustered with keys to additional
* extras for some events that need to be included as additional fields in
* the event.
*/
public class VoiceIme {
// The app name to be used for logging VoiceIME events.
public static final String APP_NAME = "voiceime";
public static final int KEYBOARD_WARNING_DIALOG_SHOWN = 0;
public static final int KEYBOARD_WARNING_DIALOG_DISMISSED = 1;
public static final int KEYBOARD_WARNING_DIALOG_OK = 2;
public static final int KEYBOARD_WARNING_DIALOG_CANCEL = 3;
public static final int SETTINGS_WARNING_DIALOG_SHOWN = 4;
public static final int SETTINGS_WARNING_DIALOG_DISMISSED = 5;
public static final int SETTINGS_WARNING_DIALOG_OK = 6;
public static final int SETTINGS_WARNING_DIALOG_CANCEL = 7;
public static final int SWIPE_HINT_DISPLAYED = 8;
public static final int PUNCTUATION_HINT_DISPLAYED = 9;
public static final int CANCEL_DURING_LISTENING = 10;
public static final int CANCEL_DURING_WORKING = 11;
public static final int CANCEL_DURING_ERROR = 12;
public static final int ERROR = 13;
public static final String EXTRA_ERROR_CODE = "code"; // value should be int
public static final int START = 14;
public static final String EXTRA_START_LOCALE = "locale"; // value should be String
public static final String EXTRA_START_SWIPE = "swipe"; // value should be boolean
public static final int VOICE_INPUT_DELIVERED = 15;
public static final int N_BEST_CHOOSE = 16;
public static final String EXTRA_N_BEST_CHOOSE_INDEX = "index"; // value should be int
public static final int TEXT_MODIFIED = 17;
public static final String EXTRA_TEXT_MODIFIED_LENGTH = "length"; // value should be int
public static final String EXTRA_TEXT_MODIFIED_TYPE = "type"; // value should be int below
public static final int TEXT_MODIFIED_TYPE_CHOOSE_SUGGESTION = 1;
public static final int TEXT_MODIFIED_TYPE_TYPING_DELETION = 2;
public static final int TEXT_MODIFIED_TYPE_TYPING_INSERTION = 3;
public static final int TEXT_MODIFIED_TYPE_TYPING_INSERTION_PUNCTUATION = 4;
public static final int INPUT_ENDED = 18;
public static final int VOICE_INPUT_SETTING_ENABLED = 19;
public static final int VOICE_INPUT_SETTING_DISABLED = 20;
public static final int IME_TEXT_ACCEPTED = 21;
}
}

View File

@@ -1,61 +0,0 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.common.speech;
/**
* Utilities for voice recognition implementations.
*
* @see android.speech.RecognitionService
* @see android.speech.RecognizerIntent
*/
public class Recognition {
/**
* The key to the extra in the Bundle returned by
* android.speech.RecognizerIntent#ACTION_GET_LANGUAGE_DETAILS
* which is an ArrayList of CharSequences which are hints that can be shown to
* the user for voice actions currently supported by voice search for the user's current
* language preference for voice search (i.e., the one defined in the extra
* android.speech.RecognizerIntent#EXTRA_LANGUAGE_PREFERENCE).
*
* If this is paired with EXTRA_HINT_CONTEXT, should return a set of hints that are
* appropriate for the provided context.
*
* The CharSequences are SpannedStrings and will contain segments wrapped in
* <annotation action="true"></annotation>. This is to indicate the section of the text
* which represents the voice action, to be highlighted in the UI if so desired.
*/
public static final String EXTRA_HINT_STRINGS = "android.speech.extra.HINT_STRINGS";
/**
* The key to an extra to be included in the request intent for
* android.speech.RecognizerIntent#ACTION_GET_LANGUAGE_DETAILS.
* Should be an int of one of the values defined below. If an
* unknown int value is provided, it should be ignored.
*/
public static final String EXTRA_HINT_CONTEXT = "android.speech.extra.HINT_CONTEXT";
/**
* A set of values for EXTRA_HINT_CONTEXT.
*/
public static final int HINT_CONTEXT_UNKNOWN = 0;
public static final int HINT_CONTEXT_VOICE_SEARCH_HELP = 1;
public static final int HINT_CONTEXT_CAR_HOME = 2;
public static final int HINT_CONTEXT_LAUNCHER = 3;
private Recognition() { } // don't instantiate
}

View File

@@ -1,45 +0,0 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.common.userhappiness;
import android.content.Intent;
import android.content.Context;
import com.android.common.speech.LoggingEvents;
/**
* Metrics for User Happiness are recorded here. Each app can define when to
* call these User Happiness metrics.
*/
public class UserHappinessSignals {
/**
* Log when a user "accepted" IME text. Each application can define what
* it means to "accept" text. In the case of Gmail, pressing the "Send"
* button indicates text acceptance. We broadcast this information to
* VoiceSearch LoggingEvents and use it to aggregate VoiceIME Happiness Metrics
*/
public static void userAcceptedImeText(Context context) {
// Create a Voice IME Logging intent.
Intent i = new Intent(LoggingEvents.ACTION_LOG_EVENT);
i.putExtra(LoggingEvents.EXTRA_APP_NAME, LoggingEvents.VoiceIme.APP_NAME);
i.putExtra(LoggingEvents.EXTRA_EVENT, LoggingEvents.VoiceIme.IME_TEXT_ACCEPTED);
i.putExtra(LoggingEvents.EXTRA_CALLING_APP_NAME, context.getPackageName());
i.putExtra(LoggingEvents.EXTRA_TIMESTAMP, System.currentTimeMillis());
context.sendBroadcast(i);
}
}

View File

@@ -1,28 +0,0 @@
# Copyright (C) 2009 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.
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_CERTIFICATE := platform
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_MODULE_TAGS := tests
LOCAL_PACKAGE_NAME := AndroidCommonTests
LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_STATIC_JAVA_LIBRARIES := android-common
LOCAL_PROGUARD_ENABLED := disabled
include $(BUILD_PACKAGE)

View File

@@ -1,30 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.common.tests"
android:sharedUserId="com.android.uid.test">
<application>
<uses-library android:name="android.test.runner" />
</application>
<!-- Run tests with "runtest common" -->
<instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.android.common.tests"
android:label="Android Common Library Tests" />
</manifest>

View File

@@ -1,219 +0,0 @@
/*
* Copyright (C) 2009 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.common;
import android.content.SharedPreferences;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
public class OperationSchedulerTest extends AndroidTestCase {
/**
* OperationScheduler subclass which uses an artificial time.
* Set {@link #timeMillis} to whatever value you like.
*/
private class TimeTravelScheduler extends OperationScheduler {
static final long DEFAULT_TIME = 1250146800000L; // 13-Aug-2009, 12:00:00 am
public long timeMillis = DEFAULT_TIME;
@Override
protected long currentTimeMillis() { return timeMillis; }
public TimeTravelScheduler() { super(getFreshStorage()); }
}
private SharedPreferences getFreshStorage() {
SharedPreferences sp = getContext().getSharedPreferences("OperationSchedulerTest", 0);
sp.edit().clear().commit();
return sp;
}
@MediumTest
public void testScheduler() throws Exception {
TimeTravelScheduler scheduler = new TimeTravelScheduler();
OperationScheduler.Options options = new OperationScheduler.Options();
assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
assertEquals(0, scheduler.getLastSuccessTimeMillis());
assertEquals(0, scheduler.getLastAttemptTimeMillis());
long beforeTrigger = scheduler.timeMillis;
scheduler.setTriggerTimeMillis(beforeTrigger + 1000000);
assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
// It will schedule for the later of the trigger and the moratorium...
scheduler.setMoratoriumTimeMillis(beforeTrigger + 500000);
assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
scheduler.setMoratoriumTimeMillis(beforeTrigger + 1500000);
assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
// Test enable/disable toggle
scheduler.setEnabledState(false);
assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
scheduler.setEnabledState(true);
assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
// Backoff interval after an error
long beforeError = (scheduler.timeMillis += 100);
scheduler.onTransientError();
assertEquals(0, scheduler.getLastSuccessTimeMillis());
assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
options.backoffFixedMillis = 1000000;
options.backoffIncrementalMillis = 500000;
assertEquals(beforeError + 1500000, scheduler.getNextTimeMillis(options));
// Two errors: backoff interval increases
beforeError = (scheduler.timeMillis += 100);
scheduler.onTransientError();
assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
assertEquals(beforeError + 2000000, scheduler.getNextTimeMillis(options));
// Reset transient error: no backoff interval
scheduler.resetTransientError();
assertEquals(0, scheduler.getLastSuccessTimeMillis());
assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
// Permanent error holds true even if transient errors are reset
// However, we remember that the transient error was reset...
scheduler.onPermanentError();
assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
scheduler.resetTransientError();
assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
scheduler.resetPermanentError();
assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
// Success resets the trigger
long beforeSuccess = (scheduler.timeMillis += 100);
scheduler.onSuccess();
assertEquals(beforeSuccess, scheduler.getLastAttemptTimeMillis());
assertEquals(beforeSuccess, scheduler.getLastSuccessTimeMillis());
assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
// The moratorium is not reset by success!
scheduler.setTriggerTimeMillis(0);
assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
scheduler.setMoratoriumTimeMillis(0);
assertEquals(beforeSuccess, scheduler.getNextTimeMillis(options));
// Periodic interval after success
options.periodicIntervalMillis = 250000;
scheduler.setTriggerTimeMillis(Long.MAX_VALUE);
assertEquals(beforeSuccess + 250000, scheduler.getNextTimeMillis(options));
// Trigger minimum is also since the last success
options.minTriggerMillis = 1000000;
assertEquals(beforeSuccess + 1000000, scheduler.getNextTimeMillis(options));
}
@SmallTest
public void testParseOptions() throws Exception {
OperationScheduler.Options options = new OperationScheduler.Options();
assertEquals(
"OperationScheduler.Options[backoff=0.0+5.0 max=86400.0 min=0.0 period=3600.0]",
OperationScheduler.parseOptions("3600", options).toString());
assertEquals(
"OperationScheduler.Options[backoff=0.0+2.5 max=86400.0 min=0.0 period=3700.0]",
OperationScheduler.parseOptions("backoff=+2.5 3700", options).toString());
assertEquals(
"OperationScheduler.Options[backoff=10.0+2.5 max=12345.6 min=7.0 period=3800.0]",
OperationScheduler.parseOptions("max=12345.6 min=7 backoff=10 period=3800",
options).toString());
assertEquals(
"OperationScheduler.Options[backoff=10.0+2.5 max=12345.6 min=7.0 period=3800.0]",
OperationScheduler.parseOptions("", options).toString());
}
@SmallTest
public void testMoratoriumWithHttpDate() throws Exception {
TimeTravelScheduler scheduler = new TimeTravelScheduler();
OperationScheduler.Options options = new OperationScheduler.Options();
long beforeTrigger = scheduler.timeMillis;
scheduler.setTriggerTimeMillis(beforeTrigger + 1000000);
assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
scheduler.setMoratoriumTimeMillis(beforeTrigger + 2000000);
assertEquals(beforeTrigger + 2000000, scheduler.getNextTimeMillis(options));
long beforeMoratorium = scheduler.timeMillis;
assertTrue(scheduler.setMoratoriumTimeHttp("3000"));
long afterMoratorium = scheduler.timeMillis;
assertTrue(beforeMoratorium + 3000000 <= scheduler.getNextTimeMillis(options));
assertTrue(afterMoratorium + 3000000 >= scheduler.getNextTimeMillis(options));
options.maxMoratoriumMillis = Long.MAX_VALUE / 2;
assertTrue(scheduler.setMoratoriumTimeHttp("Fri, 31 Dec 2030 23:59:59 GMT"));
assertEquals(1924991999000L, scheduler.getNextTimeMillis(options));
assertFalse(scheduler.setMoratoriumTimeHttp("not actually a date"));
}
@SmallTest
public void testClockRollbackScenario() throws Exception {
TimeTravelScheduler scheduler = new TimeTravelScheduler();
OperationScheduler.Options options = new OperationScheduler.Options();
options.minTriggerMillis = 2000;
// First, set up a scheduler with reasons to wait: a transient
// error with backoff and a moratorium for a few minutes.
long beforeTrigger = scheduler.timeMillis;
long triggerTime = beforeTrigger - 10000000;
scheduler.setTriggerTimeMillis(triggerTime);
assertEquals(triggerTime, scheduler.getNextTimeMillis(options));
assertEquals(0, scheduler.getLastAttemptTimeMillis());
long beforeSuccess = (scheduler.timeMillis += 100);
scheduler.onSuccess();
scheduler.setTriggerTimeMillis(triggerTime);
assertEquals(beforeSuccess, scheduler.getLastAttemptTimeMillis());
assertEquals(beforeSuccess + 2000, scheduler.getNextTimeMillis(options));
long beforeError = (scheduler.timeMillis += 100);
scheduler.onTransientError();
assertEquals(beforeError, scheduler.getLastAttemptTimeMillis());
assertEquals(beforeError + 5000, scheduler.getNextTimeMillis(options));
long beforeMoratorium = (scheduler.timeMillis += 100);
scheduler.setMoratoriumTimeMillis(beforeTrigger + 1000000);
assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
// Now set the time back a few seconds.
// The moratorium time should still be honored.
long beforeRollback = (scheduler.timeMillis = beforeTrigger - 10000);
assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
// The rollback also moved the last-attempt clock back to the rollback time.
assertEquals(scheduler.timeMillis, scheduler.getLastAttemptTimeMillis());
// But if we set the time back more than a day, the moratorium
// resets to the maximum moratorium (a day, by default), exposing
// the original trigger time.
beforeRollback = (scheduler.timeMillis = beforeTrigger - 100000000);
assertEquals(triggerTime, scheduler.getNextTimeMillis(options));
assertEquals(beforeRollback, scheduler.getLastAttemptTimeMillis());
// If we roll forward until after the re-set moratorium, then it expires.
scheduler.timeMillis = triggerTime + 5000000;
assertEquals(triggerTime, scheduler.getNextTimeMillis(options));
assertEquals(beforeRollback, scheduler.getLastAttemptTimeMillis());
assertEquals(beforeRollback, scheduler.getLastSuccessTimeMillis());
}
}

View File

@@ -1,144 +0,0 @@
#!/usr/bin/env python
from urllib2 import urlopen
TLD_PREFIX = r"""
/**
* Regular expression to match all IANA top-level domains.
* List accurate as of 2010/02/05. List taken from:
* http://data.iana.org/TLD/tlds-alpha-by-domain.txt
* This pattern is auto-generated by frameworks/base/common/tools/make-iana-tld-pattern.py
*/
public static final String TOP_LEVEL_DOMAIN_STR =
"""
TLD_SUFFIX = '";'
URL_PREFIX = r"""
/**
* Regular expression to match all IANA top-level domains for WEB_URL.
* List accurate as of 2010/02/05. List taken from:
* http://data.iana.org/TLD/tlds-alpha-by-domain.txt
* This pattern is auto-generated by frameworks/base/common/tools/make-iana-tld-pattern.py
*/
public static final String TOP_LEVEL_DOMAIN_STR_FOR_WEB_URL =
"(?:"
"""
URL_SUFFIX = ';'
class Bucket:
def __init__(self, baseLetter):
self.base=baseLetter
self.words=[]
self.letters=[]
def dump(self, isWebUrl=False, isFirst=False, isLast=False):
if (len(self.words) == 0) and (len(self.letters) == 0):
return ''
self.words.sort()
self.letters.sort()
output = ' ';
if isFirst:
if isWebUrl:
output += '+ "'
else:
output += '"('
else:
output += '+ "|'
if len(self.words) != 0:
output += '('
if isWebUrl:
output += '?:'
firstWord = 1
for word in self.words:
if firstWord == 0:
output += '|'
firstWord = 0
for letter in word:
if letter == '-':
output += '\\\\' # escape the '-' character.
output += letter
if len(self.words) > 0 and len(self.letters) > 0:
output += '|'
if len(self.letters) == 1:
output += '%c%c' % (self.base, self.letters[0])
elif len(self.letters) > 0:
output += '%c[' % self.base
for letter in self.letters:
output += letter
output += ']'
if len(self.words) != 0:
output += ')'
if not isLast:
output += '"'
output += '\n'
return output;
def add(self, line):
length = len(line)
if line.startswith('#') or (length == 0):
return;
if length == 2:
self.letters.append(line[1:2])
else:
self.words.append(line)
def getBucket(buckets, line):
letter = line[0]
bucket = buckets.get(letter)
if bucket is None:
bucket = Bucket(letter)
buckets[letter] = bucket
return bucket
def makePattern(prefix, suffix, buckets, isWebUrl=False):
output = prefix
output += getBucket(buckets, 'a').dump(isFirst=True, isWebUrl=isWebUrl)
for letter in range(ord('b'), ord('z')):
output += getBucket(buckets, chr(letter)).dump(isWebUrl=isWebUrl)
output += getBucket(buckets, 'z').dump(isLast=True, isWebUrl=isWebUrl)
if isWebUrl:
output += '))"'
else:
output += ')'
output += suffix
print output
if __name__ == "__main__":
f = urlopen('http://data.iana.org/TLD/tlds-alpha-by-domain.txt')
domains = f.readlines()
f.close()
buckets = {}
for domain in domains:
domain = domain.lower()
if len(domain) > 0:
getBucket(buckets, domain[0]).add(domain.strip())
makePattern(TLD_PREFIX, TLD_SUFFIX, buckets, isWebUrl=False)
makePattern(URL_PREFIX, URL_SUFFIX, buckets, isWebUrl=True)