diff options
20 files changed, 474 insertions, 150 deletions
diff --git a/api/current.xml b/api/current.xml index 1142088..b690e42 100644 --- a/api/current.xml +++ b/api/current.xml @@ -14581,6 +14581,25 @@ <parameter name="key" type="java.lang.String"> </parameter> </method> +<method name="hasFeatures" + return="android.accounts.AccountManagerFuture<java.lang.Boolean>" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="account" type="android.accounts.Account"> +</parameter> +<parameter name="features" type="java.lang.String[]"> +</parameter> +<parameter name="callback" type="android.accounts.AccountManagerCallback<java.lang.Boolean>"> +</parameter> +<parameter name="handler" type="android.os.Handler"> +</parameter> +</method> <method name="invalidateAuthToken" return="void" abstract="false" @@ -14690,25 +14709,6 @@ <parameter name="value" type="java.lang.String"> </parameter> </method> -<method name="testHasFeatures" - return="android.accounts.AccountManagerFuture<java.lang.Boolean>" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="account" type="android.accounts.Account"> -</parameter> -<parameter name="features" type="java.lang.String[]"> -</parameter> -<parameter name="callback" type="android.accounts.AccountManagerCallback<java.lang.Boolean>"> -</parameter> -<parameter name="handler" type="android.os.Handler"> -</parameter> -</method> <method name="updateCredentials" return="android.accounts.AccountManagerFuture<android.os.Bundle>" abstract="false" @@ -20739,6 +20739,19 @@ <parameter name="onKeyListener" type="android.content.DialogInterface.OnKeyListener"> </parameter> </method> +<method name="setOnShowListener" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="listener" type="android.content.DialogInterface.OnShowListener"> +</parameter> +</method> <method name="setOwnerActivity" return="void" abstract="false" @@ -34002,6 +34015,27 @@ </parameter> </method> </interface> +<interface name="DialogInterface.OnShowListener" + abstract="true" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<method name="onShow" + return="void" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="dialog" type="android.content.DialogInterface"> +</parameter> +</method> +</interface> <class name="Entity" extends="java.lang.Object" abstract="false" @@ -119402,6 +119436,19 @@ visibility="public" > </constructor> +<method name="getLastOutgoingCall" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="context" type="android.content.Context"> +</parameter> +</method> <field name="CACHED_NAME" type="java.lang.String" transient="false" diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index 3bbfce8..414d963 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -254,12 +254,12 @@ public class AccountManager { * The future result is a {@link Boolean} that is true if the account exists and has the * specified features. */ - public AccountManagerFuture<Boolean> testHasFeatures(final Account account, + public AccountManagerFuture<Boolean> hasFeatures(final Account account, final String[] features, AccountManagerCallback<Boolean> callback, Handler handler) { return new Future2Task<Boolean>(handler, callback) { public void doWork() throws RemoteException { - mService.testHasFeatures(mResponse, account, features); + mService.hasFeatures(mResponse, account, features); } public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException { if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) { diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java index f5166c2..ee26d3c 100644 --- a/core/java/android/accounts/AccountManagerService.java +++ b/core/java/android/accounts/AccountManagerService.java @@ -449,7 +449,7 @@ public class AccountManagerService return db.insert(TABLE_EXTRAS, EXTRAS_KEY, values); } - public void testHasFeatures(IAccountManagerResponse response, + public void hasFeatures(IAccountManagerResponse response, Account account, String[] features) { checkReadAccountsPermission(); long identityToken = clearCallingIdentity(); @@ -501,7 +501,7 @@ public class AccountManagerService } protected String toDebugString(long now) { - return super.toDebugString(now) + ", testHasFeatures" + return super.toDebugString(now) + ", hasFeatures" + ", " + mAccount + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null); } diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl index cbd26ee..36a5653 100644 --- a/core/java/android/accounts/IAccountManager.aidl +++ b/core/java/android/accounts/IAccountManager.aidl @@ -31,8 +31,7 @@ interface IAccountManager { String getUserData(in Account account, String key); AuthenticatorDescription[] getAuthenticatorTypes(); Account[] getAccounts(String accountType); - void testHasFeatures(in IAccountManagerResponse response, in Account account, - in String[] features); + void hasFeatures(in IAccountManagerResponse response, in Account account, in String[] features); void getAccountsByFeatures(in IAccountManagerResponse response, String accountType, in String[] features); boolean addAccount(in Account account, String password, in Bundle extras); void removeAccount(in IAccountManagerResponse response, in Account account); @@ -47,7 +46,7 @@ interface IAccountManager { String authTokenType, boolean notifyOnAuthFailure, boolean expectActivityLaunch, in Bundle options); void addAcount(in IAccountManagerResponse response, String accountType, - String authTokenType, in String[] requiredFeatures, boolean expectActivityLaunch, + String authTokenType, in String[] requiredFeatures, boolean expectActivityLaunch, in Bundle options); void updateCredentials(in IAccountManagerResponse response, in Account account, String authTokenType, boolean expectActivityLaunch, in Bundle options); diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index fa5d4a8..ed38240 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -995,8 +995,7 @@ public class Dialog implements DialogInterface, Window.Callback, /** * Sets a listener to be invoked when the dialog is shown. - * - * @hide Pending API council approval + * @param listener The {@link DialogInterface.OnShowListener} to use. */ public void setOnShowListener(OnShowListener listener) { if (listener != null) { diff --git a/core/java/android/content/DialogInterface.java b/core/java/android/content/DialogInterface.java index 9f1036e..947eac6 100644 --- a/core/java/android/content/DialogInterface.java +++ b/core/java/android/content/DialogInterface.java @@ -94,7 +94,6 @@ public interface DialogInterface { /** * Interface used to allow the creator of a dialog to run some code when the * dialog is shown. - * @hide Pending API council approval */ interface OnShowListener { /** diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index b59030d..fb44a62 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -1873,8 +1873,8 @@ public class SQLiteDatabase extends SQLiteClosable { * JNI method. If entire cache is wiped out, it could cause a big GC activity * just because a (rogue) process is using the cache incorrectly. */ - Log.wtf(TAG, "Too many sql statements in database cache. Make sure your sql " + - "statements are using prepared-sql-statement syntax with '?' for" + + Log.w(TAG, "Too many sql statements in database cache. Make sure your sql " + + "statements are using prepared-sql-statement syntax with '?' for " + "bindargs, instead of using actual values"); Set<String> keySet = mCompiledQueries.keySet(); for (String s : keySet) { diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index 7854423..d52632b 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -22,6 +22,7 @@ import com.android.internal.telephony.Connection; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; +import android.database.Cursor; import android.net.Uri; import android.text.TextUtils; @@ -111,25 +112,25 @@ public class CallLog { * <P>Type: TEXT</P> */ public static final String CACHED_NAME = "name"; - + /** * The cached number type (Home, Work, etc) associated with the * phone number, if it exists. * This value is not guaranteed to be current, if the contact information * associated with this number has changed. - * <P>Type: INTEGER</P> + * <P>Type: INTEGER</P> */ public static final String CACHED_NUMBER_TYPE = "numbertype"; - + /** * The cached number label, for a custom number type, associated with the * phone number, if it exists. * This value is not guaranteed to be current, if the contact information * associated with this number has changed. - * <P>Type: TEXT</P> + * <P>Type: TEXT</P> */ public static final String CACHED_NUMBER_LABEL = "numberlabel"; - + /** * Adds a call to the call log. * @@ -137,15 +138,15 @@ public class CallLog { * if the contact is unknown. * @param context the context used to get the ContentResolver * @param number the phone number to be added to the calls db - * @param presentation the number presenting rules set by the network for + * @param presentation the number presenting rules set by the network for * "allowed", "payphone", "restricted" or "unknown" * @param callType enumerated values for "incoming", "outgoing", or "missed" * @param start time stamp for the call in milliseconds * @param duration call duration in seconds - * + * * {@hide} */ - public static Uri addCall(CallerInfo ci, Context context, String number, + public static Uri addCall(CallerInfo ci, Context context, String number, int presentation, int callType, long start, int duration) { final ContentResolver resolver = context.getContentResolver(); @@ -175,22 +176,47 @@ public class CallLog { values.put(CACHED_NUMBER_TYPE, ci.numberType); values.put(CACHED_NUMBER_LABEL, ci.numberLabel); } - + if ((ci != null) && (ci.person_id > 0)) { ContactsContract.Contacts.markAsContacted(resolver, ci.person_id); } - + Uri result = resolver.insert(CONTENT_URI, values); - + removeExpiredEntries(context); - + return result; } - + + /** + * Query the call log database for the last dialed number. + * @param context Used to get the content resolver. + * @return The last phone number dialed (outgoing) or an empty + * string if none exist yet. + */ + public static String getLastOutgoingCall(Context context) { + final ContentResolver resolver = context.getContentResolver(); + Cursor c = null; + try { + c = resolver.query( + CONTENT_URI, + new String[] {NUMBER}, + TYPE + " = " + OUTGOING_TYPE, + null, + DEFAULT_SORT_ORDER + " LIMIT 1"); + if (c == null || !c.moveToFirst()) { + return ""; + } + return c.getString(0); + } finally { + if (c != null) c.close(); + } + } + private static void removeExpiredEntries(Context context) { final ContentResolver resolver = context.getContentResolver(); resolver.delete(CONTENT_URI, "_id IN " + - "(SELECT _id FROM calls ORDER BY " + DEFAULT_SORT_ORDER + "(SELECT _id FROM calls ORDER BY " + DEFAULT_SORT_ORDER + " LIMIT -1 OFFSET 500)", null); } } diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index 7fb9daf..93b5b4d 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -55,19 +55,21 @@ import java.io.InputStream; * </p> * <ul> * <li> - * The {@link Data} table contains all kinds of personal data: phone numbers, - * email addresses etc. The list of data kinds that can be stored in this table - * is open-ended. There is a predefined set of common kinds, but any application - * can add its own data kinds. + * A row in the {@link Data} table can store any kind of personal data, such + * as a phone number or email addresses. The set of data kinds that can be + * stored in this table is open-ended. There is a predefined set of common + * kinds, but any application can add its own data kinds. * </li> * <li> - * A row in the {@link RawContacts} table represents a set of Data describing a - * person and associated with a single account (for example, a single Gmail - * account). + * A row in the {@link RawContacts} table represents a set of data describing a + * person and associated with a single account (for example, one of the user's + * Gmail accounts). * </li> * <li> * A row in the {@link Contacts} table represents an aggregate of one or more - * RawContacts presumably describing the same person. + * RawContacts presumably describing the same person. When data in or associated with + * the RawContacts table is changed, the affected aggregate contacts are updated as + * necessary. * </li> * </ul> * <p> @@ -75,7 +77,8 @@ import java.io.InputStream; * </p> * <ul> * <li> - * {@link Groups}, which contains information about raw contact groups - the + * {@link Groups}, which contains information about raw contact groups + * such as Gmail contact groups. The * current API does not support the notion of groups spanning multiple accounts. * </li> * <li> @@ -106,11 +109,15 @@ public final class ContactsContract { public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY); /** - * An optional insert, update or delete URI parameter that allows the caller + * An optional URI parameter for insert, update, or delete queries + * that allows the caller * to specify that it is a sync adapter. The default value is false. If true - * the dirty flag is not automatically set and the "syncToNetwork" parameter - * is set to false when calling - * {@link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}. + * {@link RawContacts#DIRTY} is not automatically set and the + * "syncToNetwork" parameter is set to false when calling + * {@link + * ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}. + * This prevents an unnecessary extra synchronization, see the discussion of + * the delete operation in {@link RawContacts}. */ public static final String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter"; @@ -459,19 +466,23 @@ public final class ContactsContract { protected interface ContactNameColumns { /** - * The kind of data that is used as the display name for the contact, see - * DisplayNameSources. + * The kind of data that is used as the display name for the contact, such as + * structured name or email address. See DisplayNameSources. + * + * TODO: convert DisplayNameSources to a link after it is un-hidden */ public static final String DISPLAY_NAME_SOURCE = "display_name_source"; /** * The default text shown as the contact's display name. It is based on * available data, see {@link #DISPLAY_NAME_SOURCE}. + * + * @see ContactsContract.ContactNameColumns#DISPLAY_NAME_ALTERNATIVE */ public static final String DISPLAY_NAME_PRIMARY = "display_name"; /** - * Alternative representation of the display name. If display name is + * An alternative representation of the display name. If display name is * based on the structured name and the structured name follows * the Western full name style, then this field contains the "family name first" * version of the full name. Otherwise, it is the same as DISPLAY_NAME_PRIMARY. @@ -479,27 +490,42 @@ public final class ContactsContract { public static final String DISPLAY_NAME_ALTERNATIVE = "display_name_alt"; /** - * The type of alphabet used to capture the phonetic name. See + * The phonetic alphabet used to represent the {@link #PHONETIC_NAME}. See * PhoneticNameStyle. + * + * TODO: convert PhoneticNameStyle to a link after it is un-hidden */ public static final String PHONETIC_NAME_STYLE = "phonetic_name_style"; /** - * Pronunciation of the full name. See PhoneticNameStyle. + * <p> + * Pronunciation of the full name in the phonetic alphabet specified by + * {@link #PHONETIC_NAME_STYLE}. + * </p> + * <p> + * The value may be set manually by the user. + * This capability is is of interest only in countries + * with commonly used phonetic + * alphabets, such as Japan and Korea. See PhoneticNameStyle. + * </p> + * + * TODO: convert PhoneticNameStyle to a link after it is un-hidden */ public static final String PHONETIC_NAME = "phonetic_name"; /** * Sort key that takes into account locale-based traditions for sorting - * names in address books. More specifically, for Chinese names - * the sort key is the name's Pinyin spelling; for Japanese names + * names in address books. The default + * sort key is {@link #DISPLAY_NAME_PRIMARY}. For Chinese names + * the sort key is the name's Pinyin spelling, and for Japanese names * it is the Hiragana version of the phonetic name. */ public static final String SORT_KEY_PRIMARY = "sort_key"; /** * Sort key based on the alternative representation of the full name, - * specifically the one using the 'family name first' format for + * {@link #DISPLAY_NAME_ALTERNATIVE}. Thus for Western names, + * it is the one using the "family name first" format for * Western names. */ public static final String SORT_KEY_ALTERNATIVE = "sort_key_alt"; @@ -808,7 +834,10 @@ public final class ContactsContract { } /** - * Mark a contact as having been contacted. + * Mark a contact as having been contacted. This updates the + * {@link #TIMES_CONTACTED} and {@link #LAST_TIME_CONTACTED} for the + * contact, plus the corresponding values of any associated raw + * contacts. * * @param resolver the ContentResolver to use * @param contactId the person who was contacted @@ -1050,15 +1079,37 @@ public final class ContactsContract { } /** - * Constants for the raw contacts table, which contains the base contact - * information per sync source. Sync adapters and contact management apps + * Constants for the raw contacts table, which contains one row of contact + * information for each person in each synced account. Sync adapters and + * contact management apps * are the primary consumers of this API. + * + * <h3>Aggregation</h3> + * <p> + * As soon as a raw contact is inserted or whenever its constituent data + * changes, the provider will check if the raw contact matches other + * existing raw contacts and if so will aggregate it with those. The + * aggregation is reflected in the {@link RawContacts} table by the change of the + * {@link #CONTACT_ID} field, which is the reference to the aggregate contact. + * </p> + * <p> + * Changes to the structured name, organization, phone number, email address, + * or nickname trigger a re-aggregation. + * </p> + * <p> + * See also {@link AggregationExceptions} for a mechanism to control + * aggregation programmatically. + * </p> + * * <h3>Operations</h3> * <dl> * <dt><b>Insert</b></dt> - * <dd>There are two mechanisms that can be used to insert a raw contact: incremental and - * batch. The incremental method is more traditional but less efficient. It should be used - * only if the constituent data rows are unavailable at the time the raw contact is created: + * <dd> + * <p> + * Raw contacts can be inserted incrementally or in a batch. + * The incremental method is more traditional but less efficient. + * It should be used + * only if no {@link Data} values are available at the time the raw contact is created: * <pre> * ContentValues values = new ContentValues(); * values.put(RawContacts.ACCOUNT_TYPE, accountType); @@ -1066,9 +1117,10 @@ public final class ContactsContract { * Uri rawContactUri = getContentResolver().insert(RawContacts.CONTENT_URI, values); * long rawContactId = ContentUris.parseId(rawContactUri); * </pre> + * </p> * <p> - * Once data rows are available, insert those. For example, here's how you would insert - * a name: + * Once {@link Data} values become available, insert those. + * For example, here's how you would insert a name: * * <pre> * values.clear(); @@ -1084,6 +1136,7 @@ public final class ContactsContract { * and causes at most one aggregation pass. * <pre> * ArrayList<ContentProviderOperation> ops = Lists.newArrayList(); + * ... * int rawContactInsertIndex = ops.size(); * ops.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) * .withValue(RawContacts.ACCOUNT_TYPE, accountType) @@ -1100,21 +1153,27 @@ public final class ContactsContract { * </pre> * </p> * <p> - * Please note the use of back reference in the construction of the - * {@link ContentProviderOperation}. It allows an operation to use the result of - * a previous operation by referring to it by its index in the batch. + * Note the use of {@link ContentProviderOperation.Builder#withValueBackReference(String, int)} + * to refer to the as-yet-unknown index value of the raw contact inserted in the + * first operation. * </p> + * * <dt><b>Update</b></dt> - * <dd><p>Just as with insert, the update can be done incrementally or as a batch, the - * batch mode being the preferred method.</p></dd> + * <dd><p> + * Raw contacts can be updated incrementally or in a batch. + * Batch mode should be used whenever possible. + * The procedures and considerations are analogous to those documented above for inserts. + * </p></dd> * <dt><b>Delete</b></dt> * <dd><p>When a raw contact is deleted, all of its Data rows as well as StatusUpdates, * AggregationExceptions, PhoneLookup rows are deleted automatically. When all raw - * contacts in a Contact are deleted, the Contact itself is also deleted automatically. + * contacts associated with a {@link Contacts} row are deleted, the {@link Contacts} row + * itself is also deleted automatically. * </p> * <p> - * The invocation of {@code resolver.delete(...)}, does not physically delete - * a raw contacts row. It sets the {@link #DELETED} flag on the raw contact and + * The invocation of {@code resolver.delete(...)}, does not immediately delete + * a raw contacts row. + * Instead, it sets the {@link #DELETED} flag on the raw contact and * removes the raw contact from its aggregate contact. * The sync adapter then deletes the raw contact from the server and * finalizes phone-side deletion by calling {@code resolver.delete(...)} @@ -1124,10 +1183,11 @@ public final class ContactsContract { * is marked for deletion, it will remain on the phone. However it will be * effectively invisible, because it will not be part of any aggregate contact. * </dd> + * * <dt><b>Query</b></dt> * <dd> * <p> - * Finding all raw contacts in a Contact is easy: + * It is easy to find all raw contacts in a Contact: * <pre> * Cursor c = getContentResolver().query(RawContacts.CONTENT_URI, * new String[]{RawContacts._ID}, @@ -1136,7 +1196,7 @@ public final class ContactsContract { * </pre> * </p> * <p> - * There are two ways to find raw contacts within a specific account, + * To find raw contacts within a specific account, * you can either put the account name and type in the selection or pass them as query * parameters. The latter approach is preferable, especially when you can reuse the * URI: @@ -1178,19 +1238,11 @@ public final class ContactsContract { * </p> * </dd> * </dl> - * <h3>Aggregation</h3> - * <p> - * As soon as a raw contact is inserted or whenever its constituent data - * changes, the provider will check if the raw contact matches other - * existing raw contacts and if so will aggregate it with those. From the - * data standpoint, aggregation is reflected in the change of the - * {@link #CONTACT_ID} field, which is the reference to the aggregate contact. - * </p> - * <p> - * See also {@link AggregationExceptions} for a mechanism to control - * aggregation programmatically. - * </p> * <h2>Columns</h2> + * TODO: include {@link #DISPLAY_NAME_PRIMARY}, {@link #DISPLAY_NAME_ALTERNATIVE}, + * {@link #DISPLAY_NAME_SOURCE}, {@link #PHONETIC_NAME}, {@link #PHONETIC_NAME_STYLE}, + * {@link #SORT_KEY_PRIMARY}, {@link #SORT_KEY_ALTERNATIVE}? + * * <table class="jd-sumtable"> * <tr> * <th colspan='4'>RawContacts</th> @@ -1199,15 +1251,16 @@ public final class ContactsContract { * <td>long</td> * <td>{@link #_ID}</td> * <td>read-only</td> - * <td>Row ID. Sync adapter should try to preserve row IDs during updates. In other words, - * it would be a really bad idea to delete and reinsert a raw contact. A sync adapter should - * always do an update instead.</td> + * <td>Row ID. Sync adapters should try to preserve row IDs during updates. In other words, + * it is much better for a sync adapter to update a raw contact rather than to delete and + * re-insert it.</td> * </tr> * <tr> * <td>long</td> * <td>{@link #CONTACT_ID}</td> * <td>read-only</td> - * <td>A reference to the {@link ContactsContract.Contacts#_ID} that this raw contact belongs + * <td>The ID of the row in the {@link ContactsContract.Contacts} table + * that this raw contact belongs * to. Raw contacts are linked to contacts by the aggregation process, which can be controlled * by the {@link #AGGREGATION_MODE} field and {@link AggregationExceptions}.</td> * </tr> @@ -1238,7 +1291,8 @@ public final class ContactsContract { * <td>The number of times the contact has been contacted. To have an effect * on the corresponding value of the aggregate contact, this field * should be set at the time the raw contact is inserted. - * See {@link ContactsContract.Contacts#markAsContacted}.</td> + * After that, this value is typically updated via + * {@link ContactsContract.Contacts#markAsContacted}.</td> * </tr> * <tr> * <td>long</td> @@ -1247,14 +1301,16 @@ public final class ContactsContract { * <td>The timestamp of the last time the contact was contacted. To have an effect * on the corresponding value of the aggregate contact, this field * should be set at the time the raw contact is inserted. - * See {@link ContactsContract.Contacts#markAsContacted}.</td> + * After that, this value is typically updated via + * {@link ContactsContract.Contacts#markAsContacted}. + * </td> * </tr> * <tr> * <td>int</td> * <td>{@link #STARRED}</td> * <td>read/write</td> * <td>An indicator for favorite contacts: '1' if favorite, '0' otherwise. - * Changing this field immediately effects the corresponding aggregate contact: + * Changing this field immediately affects the corresponding aggregate contact: * if any raw contacts in that aggregate contact are starred, then the contact * itself is marked as starred.</td> * </tr> @@ -1267,7 +1323,8 @@ public final class ContactsContract { * {@link android.media.RingtoneManager#ACTION_RINGTONE_PICKER} intent. * To have an effect on the corresponding value of the aggregate contact, this field * should be set at the time the raw contact is inserted. To set a custom - * ringtone on a contact, use the field {@link ContactsContract.Contacts#CUSTOM_RINGTONE} + * ringtone on a contact, use the field {@link ContactsContract.Contacts#CUSTOM_RINGTONE + * Contacts.CUSTOM_RINGTONE} * instead.</td> * </tr> * <tr> @@ -1284,16 +1341,27 @@ public final class ContactsContract { * <td>{@link #ACCOUNT_NAME}</td> * <td>read/write-once</td> * <td>The name of the account instance to which this row belongs, which when paired with - * {@link #ACCOUNT_TYPE} identifies a specific account. It should be set at the time + * {@link #ACCOUNT_TYPE} identifies a specific account. + * For example, this will be the Gmail address if it is a Google account. + * It should be set at the time * the raw contact is inserted and never changed afterwards.</td> * </tr> * <tr> * <td>String</td> * <td>{@link #ACCOUNT_TYPE}</td> * <td>read/write-once</td> - * <td>The type of account to which this row belongs, which when paired with - * {@link #ACCOUNT_NAME} identifies a specific account. It should be set at the time - * the raw contact is inserted and never changed afterwards.</td> + * <td> + * <p> + * The type of account to which this row belongs, which when paired with + * {@link #ACCOUNT_NAME} identifies a specific account. + * It should be set at the time + * the raw contact is inserted and never changed afterwards. + * </p> + * <p> + * To ensure uniqueness, new account types should be chosen according to the + * Java package naming convention. Thus a Google account is of type "com.google". + * </p> + * </td> * </tr> * <tr> * <td>String</td> @@ -1302,8 +1370,8 @@ public final class ContactsContract { * <td>String that uniquely identifies this row to its source account. * Typically it is set at the time the raw contact is inserted and never * changed afterwards. The one notable exception is a new raw contact: it - * will have an account name and type, but no source id. This should - * indicated to the sync adapter that a new contact needs to be created + * will have an account name and type, but no source id. This + * indicates to the sync adapter that a new contact needs to be created * server-side and its ID stored in the corresponding SOURCE_ID field on * the phone. * </td> @@ -1335,7 +1403,8 @@ public final class ContactsContract { * <td>String</td> * <td>{@link #SYNC1}</td> * <td>read/write</td> - * <td>Generic column for use by sync adapters. Content provider + * <td>Generic column provided for arbitrary use by sync adapters. + * The content provider * stores this information on behalf of the sync adapter but does not * interpret it in any way. * </td> @@ -1372,46 +1441,69 @@ public final class ContactsContract { } /** - * The content:// style URI for this table + * The content:// style URI for this table, which requests a directory of + * raw contact rows matching the selection criteria. */ public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "raw_contacts"); /** - * The MIME type of {@link #CONTENT_URI} providing a directory of - * people. + * The MIME type of the results from {@link #CONTENT_URI} when a specific + * ID value is not provided, and multiple raw contacts may be returned. */ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/raw_contact"; /** - * The MIME type of a {@link #CONTENT_URI} subdirectory of a single - * person. + * The MIME type of the results when a raw contact ID is appended to {@link #CONTENT_URI}, + * yielding a subdirectory of a single person. */ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/raw_contact"; /** - * Aggregation mode: aggregate asynchronously. + * Aggregation mode: aggregate immediately after insert or update operation(s) are complete. */ public static final int AGGREGATION_MODE_DEFAULT = 0; /** - * Aggregation mode: aggregate at the time the raw contact is inserted/updated. - * TODO: deprecate. Aggregation is now synchronous, this value is a no-op + * Do not use. + * + * TODO: deprecate in favor of {@link #AGGREGATION_MODE_DEFAULT} */ public static final int AGGREGATION_MODE_IMMEDIATE = 1; /** - * If {@link #AGGREGATION_MODE} is {@link #AGGREGATION_MODE_SUSPENDED}, changes - * to the raw contact do not cause its aggregation to be revisited. Note that changing + * <p> + * Aggregation mode: aggregation suspended temporarily, and is likely to be resumed later. + * Changes to the raw contact will update the associated aggregate contact but will not + * result in any change in how the contact is aggregated. Similar to + * {@link #AGGREGATION_MODE_DISABLED}, but maintains a link to the corresponding + * {@link Contacts} aggregate. + * </p> + * <p> + * This can be used to postpone aggregation until after a series of updates, for better + * performance and/or user experience. + * </p> + * <p> + * Note that changing * {@link #AGGREGATION_MODE} from {@link #AGGREGATION_MODE_SUSPENDED} to - * {@link #AGGREGATION_MODE_DEFAULT} does not trigger an aggregation pass. Any subsequent + * {@link #AGGREGATION_MODE_DEFAULT} does not trigger an aggregation pass, but any + * subsequent * change to the raw contact's data will. + * </p> */ public static final int AGGREGATION_MODE_SUSPENDED = 2; /** - * Aggregation mode: never aggregate this raw contact (note that the raw contact will not - * have a corresponding Aggregate and therefore will not be included in Aggregates - * query results.) + * <p> + * Aggregation mode: never aggregate this raw contact. The raw contact will not + * have a corresponding {@link Contacts} aggregate and therefore will not be included in + * {@link Contacts} query results. + * </p> + * <p> + * For example, this mode can be used for a raw contact that is marked for deletion while + * waiting for the deletion to occur on the server side. + * </p> + * + * @see #AGGREGATION_MODE_SUSPENDED */ public static final int AGGREGATION_MODE_DISABLED = 3; @@ -1441,9 +1533,11 @@ public final class ContactsContract { } /** - * A sub-directory of a single raw contact that contains all of their + * A sub-directory of a single raw contact that contains all of its * {@link ContactsContract.Data} rows. To access this directory * append {@link Data#CONTENT_DIRECTORY} to the contact URI. + * + * TODO: deprecate in favor of {@link RawContacts.Entity}. */ public static final class Data implements BaseColumns, DataColumns { /** @@ -1460,26 +1554,24 @@ public final class ContactsContract { /** * <p> - * A sub-directory of a single raw contact that contains all of their + * A sub-directory of a single raw contact that contains all of its * {@link ContactsContract.Data} rows. To access this directory append * {@link #CONTENT_DIRECTORY} to the contact URI. See * {@link RawContactsEntity} for a stand-alone table containing the same * data. * </p> * <p> - * The Entity directory is similar to the {@link RawContacts.Data} - * directory but with two important differences: - * <ul> - * <li>Entity has different ID fields: {@link #_ID} for the raw contact - * and {@link #DATA_ID} for the data rows.</li> - * <li>Entity always contains at least one row, even if there are no + * Entity has two ID fields: {@link #_ID} for the raw contact + * and {@link #DATA_ID} for the data rows. + * Entity always contains at least one row, even if there are no * actual data rows. In this case the {@link #DATA_ID} field will be - * null.</li> - * </ul> - * Using Entity should preferred to using two separate queries: - * RawContacts followed by Data. The reason is that Entity reads all - * data for a raw contact in one transaction, so there is no possibility - * of the data changing between the two queries. + * null. + * </p> + * <p> + * Entity reads all + * data for a raw contact in one transaction, to guarantee + * consistency. + * </p> */ public static final class Entity implements BaseColumns, DataColumns { /** @@ -1501,6 +1593,11 @@ public final class ContactsContract { public static final String DATA_ID = "data_id"; } + /** + * TODO: javadoc + * @param cursor + * @return + */ public static EntityIterator newEntityIterator(Cursor cursor) { return new EntityIteratorImpl(cursor); } @@ -1839,7 +1936,12 @@ public final class ContactsContract { * By convention, {@link #DATA15} is used for storing BLOBs (binary data). * </p> * <p> - * Typically you should refrain from introducing new kinds of data for an other + * The sync adapter for a given account type must correctly handle every data type + * used in the corresponding raw contacts. Otherwise it could result in lost or + * corrupted data. + * </p> + * <p> + * Similarly, you should refrain from introducing new kinds of data for an other * party's account types. For example, if you add a data row for * "favorite song" to a raw contact owned by a Google account, it will not * get synced to the server, because the Google sync adapter does not know diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java index a93cee7..3774156 100644 --- a/core/java/android/provider/Downloads.java +++ b/core/java/android/provider/Downloads.java @@ -27,7 +27,7 @@ import android.net.Uri; */ // For 1.0 the download manager can't deal with abuse from untrusted apps, so // this API is hidden. -public final class Downloads implements BaseColumns { +public final class Downloads { private Downloads() {} /** diff --git a/core/java/android/webkit/JWebCoreJavaBridge.java b/core/java/android/webkit/JWebCoreJavaBridge.java index f350d13..e496d97 100644 --- a/core/java/android/webkit/JWebCoreJavaBridge.java +++ b/core/java/android/webkit/JWebCoreJavaBridge.java @@ -247,4 +247,5 @@ final class JWebCoreJavaBridge extends Handler { private native void nativeUpdatePluginDirectories(String[] directories, boolean reload); public native void setNetworkOnLine(boolean online); + public native void setNetworkType(String type, String subtype); } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 09ed931..db5641c 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -80,6 +80,7 @@ import java.io.IOException; import java.net.URLDecoder; import java.util.ArrayList; import java.util.List; +import java.util.HashMap; import java.util.Map; import junit.framework.Assert; @@ -1132,6 +1133,16 @@ public class WebView extends AbsoluteLayout } /** + * Inform WebView about the current network type. + * {@hide} + */ + public void setNetworkType(String type, String subtype) { + Map<String, String> map = new HashMap<String, String>(); + map.put("type", type); + map.put("subtype", subtype); + mWebViewCore.sendMessage(EventHub.SET_NETWORK_TYPE, map); + } + /** * Save the state of this WebView used in * {@link android.app.Activity#onSaveInstanceState}. Please note that this * method no longer stores the display data for this WebView. The previous diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 949b318..d509bb4 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -878,6 +878,8 @@ final class WebViewCore { static final int HIDE_FULLSCREEN = 182; + static final int SET_NETWORK_TYPE = 183; + // private message ids private static final int DESTROY = 200; @@ -1110,6 +1112,16 @@ final class WebViewCore { .setNetworkOnLine(msg.arg1 == 1); break; + case SET_NETWORK_TYPE: + if (BrowserFrame.sJavaBridge == null) { + throw new IllegalStateException("No WebView " + + "has been created in this process!"); + } + Map<String, String> map = (Map<String, String>) msg.obj; + BrowserFrame.sJavaBridge + .setNetworkType(map.get("type"), map.get("subtype")); + break; + case CLEAR_CACHE: clearCache(msg.arg1 == 1); break; diff --git a/keystore/java/android/security/SystemKeyStore.java b/keystore/java/android/security/SystemKeyStore.java index 452125a..61a4293 100644 --- a/keystore/java/android/security/SystemKeyStore.java +++ b/keystore/java/android/security/SystemKeyStore.java @@ -35,6 +35,7 @@ import javax.crypto.SecretKey; public class SystemKeyStore { private static final String SYSTEM_KEYSTORE_DIRECTORY = "misc/systemkeys"; + private static final String KEY_FILE_EXTENSION = ".sks"; private static SystemKeyStore mInstance = new SystemKeyStore(); private SystemKeyStore() { } @@ -43,6 +44,28 @@ public class SystemKeyStore { return mInstance; } + public static String toHexString(byte[] keyData) { + if (keyData == null) { + return null; + } + int keyLen = keyData.length; + int expectedStringLen = keyData.length * 2; + StringBuilder sb = new StringBuilder(expectedStringLen); + for (int i = 0; i < keyData.length; i++) { + String hexStr = Integer.toString(keyData[i] & 0x00FF, 16); + if (hexStr.length() == 1) { + hexStr = "0" + hexStr; + } + sb.append(hexStr); + } + return sb.toString(); + } + + public String generateNewKeyHexString(int numBits, String algName, String keyName) + throws NoSuchAlgorithmException { + return toHexString(generateNewKey(numBits, algName, keyName)); + } + public byte[] generateNewKey(int numBits, String algName, String keyName) throws NoSuchAlgorithmException { @@ -78,10 +101,14 @@ public class SystemKeyStore { private File getKeyFile(String keyName) { File sysKeystoreDir = new File(Environment.getDataDirectory(), SYSTEM_KEYSTORE_DIRECTORY); - File keyFile = new File(sysKeystoreDir, keyName); + File keyFile = new File(sysKeystoreDir, keyName + KEY_FILE_EXTENSION); return keyFile; } + public String retrieveKeyHexString(String keyName) { + return toHexString(retrieveKey(keyName)); + } + public byte[] retrieveKey(String keyName) { File keyFile = getKeyFile(keyName); diff --git a/keystore/tests/src/android/security/SystemKeyStoreTest.java b/keystore/tests/src/android/security/SystemKeyStoreTest.java index a85f889..bbeceeb 100644 --- a/keystore/tests/src/android/security/SystemKeyStoreTest.java +++ b/keystore/tests/src/android/security/SystemKeyStoreTest.java @@ -32,6 +32,7 @@ import android.test.suitebuilder.annotation.MediumTest; public class SystemKeyStoreTest extends ActivityUnitTestCase<Activity> { private static final String keyName = "TestKey"; + private static final String keyName2 = "TestKey2"; private SystemKeyStore mSysKeyStore = null; public SystemKeyStoreTest() { @@ -43,6 +44,7 @@ public class SystemKeyStoreTest extends ActivityUnitTestCase<Activity> { mSysKeyStore = SystemKeyStore.getInstance(); try { mSysKeyStore.deleteKey(keyName); + mSysKeyStore.deleteKey(keyName2); } catch (Exception e) { } super.setUp(); } @@ -51,6 +53,7 @@ public class SystemKeyStoreTest extends ActivityUnitTestCase<Activity> { protected void tearDown() throws Exception { try { mSysKeyStore.deleteKey(keyName); + mSysKeyStore.deleteKey(keyName2); } catch (Exception e) { } super.tearDown(); } @@ -67,6 +70,14 @@ public class SystemKeyStoreTest extends ActivityUnitTestCase<Activity> { mSysKeyStore.deleteKey(keyName); byte[] nullKey = mSysKeyStore.retrieveKey(keyName); assertNull(nullKey); + + String newKeyStr = mSysKeyStore.generateNewKeyHexString(128, "AES", keyName2); + assertNotNull(newKeyStr); + String recKeyStr = mSysKeyStore.retrieveKeyHexString(keyName2); + assertEquals(newKeyStr, recKeyStr); + mSysKeyStore.deleteKey(keyName2); + String nullKey2 = mSysKeyStore.retrieveKeyHexString(keyName); + assertNull(nullKey2); } catch (Exception e) { fail(); } diff --git a/opengl/libagl/Android.mk b/opengl/libagl/Android.mk index 9837845..c2e9f31 100644 --- a/opengl/libagl/Android.mk +++ b/opengl/libagl/Android.mk @@ -39,6 +39,11 @@ endif ifneq ($(TARGET_SIMULATOR),true) # we need to access the private Bionic header <bionic_tls.h> + # on ARM platforms, we need to mirror the ARCH_ARM_HAVE_TLS_REGISTER + # behavior from the bionic Android.mk file + ifeq ($(TARGET_ARCH)-$(ARCH_ARM_HAVE_TLS_REGISTER),arm-true) + LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER + endif LOCAL_C_INCLUDES += bionic/libc/private endif diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk index 7353385..6b7020f 100644 --- a/opengl/libs/Android.mk +++ b/opengl/libs/Android.mk @@ -20,6 +20,11 @@ LOCAL_MODULE:= libEGL ifeq ($(TARGET_SIMULATOR),true) else LOCAL_SHARED_LIBRARIES += libdl + # Bionic's private TLS header relies on the ARCH_ARM_HAVE_TLS_REGISTER to + # select the appropriate TLS codepath + ifeq ($(ARCH_ARM_HAVE_TLS_REGISTER),true) + LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER + endif # we need to access the private Bionic header <bionic_tls.h> LOCAL_C_INCLUDES += bionic/libc/private endif @@ -75,6 +80,9 @@ ifeq ($(TARGET_SIMULATOR),true) else LOCAL_SHARED_LIBRARIES += libdl # we need to access the private Bionic header <bionic_tls.h> + ifeq ($(ARCH_ARM_HAVE_TLS_REGISTER),true) + LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER + endif LOCAL_C_INCLUDES += bionic/libc/private endif @@ -108,6 +116,9 @@ ifeq ($(TARGET_SIMULATOR),true) else LOCAL_SHARED_LIBRARIES += libdl # we need to access the private Bionic header <bionic_tls.h> + ifeq ($(ARCH_ARM_HAVE_TLS_REGISTER),true) + LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER + endif LOCAL_C_INCLUDES += bionic/libc/private endif diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 80129d0..5a8d35f 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -180,6 +180,15 @@ public class ConnectivityService extends IConnectivityManager.Stub { private ConnectivityService(Context context) { if (DBG) Log.v(TAG, "ConnectivityService starting up"); + + // setup our unique device name + String id = Settings.Secure.getString(context.getContentResolver(), + Settings.Secure.ANDROID_ID); + if (id != null && id.length() > 0) { + String name = new String("android_").concat(id); + SystemProperties.set("net.hostname", name); + } + mContext = context; mNetTrackers = new NetworkStateTracker[ ConnectivityManager.MAX_NETWORK_TYPE+1]; @@ -1248,11 +1257,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { info = (NetworkInfo) msg.obj; int type = info.getType(); NetworkInfo.State state = info.getState(); - if(mNetAttributes[type].mLastState == state) { + if (mNetAttributes[type].mLastState == state) { if (DBG) { // TODO - remove this after we validate the dropping doesn't break anything Log.d(TAG, "Dropping ConnectivityChange for " + - info.getTypeName() +": " + + info.getTypeName() + ": " + state + "/" + info.getDetailedState()); } return; diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 5566979..170477f 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -455,7 +455,9 @@ class PackageManagerService extends IPackageManager.Stub { EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START, startTime); - int scanMode = SCAN_MONITOR; + // Set flag to monitor and not change apk file paths when + // scanning install directories. + int scanMode = SCAN_MONITOR | SCAN_NO_PATHS; if (mNoDexOpt) { Log.w(TAG, "Running ENG build: no pre-dexopt!"); scanMode |= SCAN_NO_DEX; @@ -573,12 +575,12 @@ class PackageManagerService extends IPackageManager.Stub { mFrameworkDir.getPath(), OBSERVER_EVENTS, true); mFrameworkInstallObserver.startWatching(); scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM, - scanMode | SCAN_NO_DEX | SCAN_NO_PATHS); + scanMode | SCAN_NO_DEX); mSystemAppDir = new File(Environment.getRootDirectory(), "app"); mSystemInstallObserver = new AppDirObserver( mSystemAppDir.getPath(), OBSERVER_EVENTS, true); mSystemInstallObserver.startWatching(); - scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM, scanMode | SCAN_NO_PATHS); + scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM, scanMode); mAppInstallDir = new File(dataDir, "app"); if (mInstaller == null) { // Make sure these dirs exist, when we are running in @@ -3754,7 +3756,7 @@ class PackageManagerService extends IPackageManager.Stub { (mIsRom ? PackageParser.PARSE_IS_SYSTEM : 0) | PackageParser.PARSE_CHATTY | PackageParser.PARSE_MUST_BE_APK, - SCAN_MONITOR); + SCAN_MONITOR | SCAN_NO_PATHS); if (p != null) { synchronized (mPackages) { grantPermissionsLP(p, false); diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java index bee0930..42c0254 100644 --- a/services/java/com/android/server/status/StatusBarPolicy.java +++ b/services/java/com/android/server/status/StatusBarPolicy.java @@ -43,6 +43,9 @@ import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.TelephonyManager; import android.text.format.DateFormat; +import android.text.style.RelativeSizeSpan; +import android.text.Spannable; +import android.text.SpannableStringBuilder; import android.util.Log; import android.view.View; import android.view.ViewGroup; @@ -61,6 +64,7 @@ import com.android.internal.telephony.cdma.EriInfo; import com.android.internal.telephony.cdma.TtyIntent; import com.android.server.am.BatteryStatsService; +import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.TimeZone; @@ -532,10 +536,69 @@ public class StatusBarPolicy { sInstance = new StatusBarPolicy(context, service); } + private final CharSequence getSmallTime() { + boolean b24 = DateFormat.is24HourFormat(mContext); + int res; + + if (b24) { + res = R.string.twenty_four_hour_time_format; + } else { + res = R.string.twelve_hour_time_format; + } + + String format = mContext.getString(res); + + /* + * Search for an unquoted "a" in the format string, so we can + * add dummy characters around it to let us find it again after + * formatting and change its size. + */ + int a = -1; + boolean quoted = false; + for (int i = 0; i < format.length(); i++) { + char c = format.charAt(i); + + if (c == '\'') { + quoted = !quoted; + } + + if (!quoted && c == 'a') { + a = i; + break; + } + } + + final char MAGIC1 = '\uEF00'; + final char MAGIC2 = '\uEF01'; + + if (a >= 0) { + format = format.substring(0, a) + MAGIC1 + "a" + MAGIC2 + + format.substring(a + 1); + } + + String result = new SimpleDateFormat(format).format(mCalendar.getTime()); + + int magic1 = result.indexOf(MAGIC1); + int magic2 = result.indexOf(MAGIC2); + + if (magic1 >= 0 && magic2 > magic1) { + SpannableStringBuilder formatted = new SpannableStringBuilder(result); + + formatted.setSpan(new RelativeSizeSpan(0.7f), magic1, magic2, + Spannable.SPAN_EXCLUSIVE_INCLUSIVE); + + formatted.delete(magic2, magic2 + 1); + formatted.delete(magic1, magic1 + 1); + + return formatted; + } else { + return result; + } + } + private final void updateClock() { mCalendar.setTimeInMillis(System.currentTimeMillis()); - mClockData.text = DateFormat.getTimeFormat(mContext) - .format(mCalendar.getTime()); + mClockData.text = getSmallTime(); mService.updateIcon(mClockIcon, mClockData, null); } |
