diff options
| -rwxr-xr-x | core/java/android/gesture/GestureUtilities.java | 111 | ||||
| -rw-r--r-- | core/java/android/provider/ContactsContract.java | 292 | ||||
| -rw-r--r-- | services/java/com/android/server/PackageManagerService.java | 13 |
3 files changed, 266 insertions, 150 deletions
diff --git a/core/java/android/gesture/GestureUtilities.java b/core/java/android/gesture/GestureUtilities.java index f1dcd89d3bab..d6d48992dfd0 100755 --- a/core/java/android/gesture/GestureUtilities.java +++ b/core/java/android/gesture/GestureUtilities.java @@ -27,7 +27,6 @@ import java.io.IOException; import static android.gesture.GestureConstants.*; final class GestureUtilities { - private static final int TEMPORAL_SAMPLING_RATE = 16; private GestureUtilities() { } @@ -51,29 +50,29 @@ final class GestureUtilities { final float targetPatchSize = sampleMatrixDimension - 1; // edge inclusive float[] sample = new float[sampleMatrixDimension * sampleMatrixDimension]; Arrays.fill(sample, 0); - + RectF rect = gesture.getBoundingBox(); float sx = targetPatchSize / rect.width(); float sy = targetPatchSize / rect.height(); float scale = sx < sy ? sx : sy; - + float preDx = -rect.centerX(); float preDy = -rect.centerY(); float postDx = targetPatchSize / 2; float postDy = targetPatchSize / 2; - + final ArrayList<GestureStroke> strokes = gesture.getStrokes(); final int count = strokes.size(); - + int size; float xpos; float ypos; - + for (int index = 0; index < count; index++) { final GestureStroke stroke = strokes.get(index); float[] strokepoints = stroke.points; size = strokepoints.length; - + final float[] pts = new float[size]; for (int i = 0; i < size; i += 2) { @@ -118,7 +117,7 @@ final class GestureUtilities { xpos++; } } - + // evaluating vertically if (segmentEndY > segmentStartY) { ypos = (float) Math.ceil(segmentStartY); @@ -143,11 +142,11 @@ final class GestureUtilities { segmentEndY = segmentStartY; } } - - + + return sample; } - + private static void plot(float x, float y, float[] sample, int sampleSize) { x = x < 0 ? 0 : x; y = y < 0 ? 0 : y; @@ -286,8 +285,8 @@ final class GestureUtilities { * @param points * @return the covariance matrix */ - private static double[][] computeCoVariance(float[] points) { - double[][] array = new double[2][2]; + private static float[][] computeCoVariance(float[] points) { + float[][] array = new float[2][2]; array[0][0] = 0; array[0][1] = 0; array[1][0] = 0; @@ -321,17 +320,17 @@ final class GestureUtilities { return sum; } - static double computeStraightness(float[] points) { + static float computeStraightness(float[] points) { float totalLen = computeTotalLength(points); float dx = points[2] - points[0]; float dy = points[3] - points[1]; - return Math.sqrt(dx * dx + dy * dy) / totalLen; + return (float) Math.sqrt(dx * dx + dy * dy) / totalLen; } - static double computeStraightness(float[] points, float totalLen) { + static float computeStraightness(float[] points, float totalLen) { float dx = points[2] - points[0]; float dy = points[3] - points[1]; - return Math.sqrt(dx * dx + dy * dy) / totalLen; + return (float) Math.sqrt(dx * dx + dy * dy) / totalLen; } /** @@ -341,8 +340,8 @@ final class GestureUtilities { * @param vector2 * @return the distance */ - static double squaredEuclideanDistance(float[] vector1, float[] vector2) { - double squaredDistance = 0; + static float squaredEuclideanDistance(float[] vector1, float[] vector2) { + float squaredDistance = 0; int size = vector1.length; for (int i = 0; i < size; i++) { float difference = vector1[i] - vector2[i]; @@ -358,13 +357,13 @@ final class GestureUtilities { * @param vector2 * @return the distance between 0 and Math.PI */ - static double cosineDistance(float[] vector1, float[] vector2) { + static float cosineDistance(float[] vector1, float[] vector2) { float sum = 0; int len = vector1.length; for (int i = 0; i < len; i++) { sum += vector1[i] * vector2[i]; } - return Math.acos(sum); + return (float) Math.acos(sum); } /** @@ -375,46 +374,58 @@ final class GestureUtilities { * @param numOrientations the maximum number of orientation allowed * @return the distance between the two instances (between 0 and Math.PI) */ - static double minimumCosineDistance(float[] vector1, float[] vector2, int numOrientations) { + static float minimumCosineDistance(float[] vector1, float[] vector2, int numOrientations) { final int len = vector1.length; - double a = 0; - double b = 0; + float a = 0; + float b = 0; for (int i = 0; i < len; i += 2) { a += vector1[i] * vector2[i] + vector1[i + 1] * vector2[i + 1]; b += vector1[i] * vector2[i + 1] - vector1[i + 1] * vector2[i]; } if (a != 0) { - final double tan = b/a; + final float tan = b/a; final double angle = Math.atan(tan); if (numOrientations > 2 && Math.abs(angle) >= Math.PI / numOrientations) { - return Math.acos(a); + return (float) Math.acos(a); } else { final double cosine = Math.cos(angle); final double sine = cosine * tan; - return Math.acos(a * cosine + b * sine); + return (float) Math.acos(a * cosine + b * sine); } } else { - return Math.PI / 2; + return (float) Math.PI / 2; } } - static OrientedBoundingBox computeOrientedBoundingBox(ArrayList<GesturePoint> pts) { - GestureStroke stroke = new GestureStroke(pts); - float[] points = temporalSampling(stroke, TEMPORAL_SAMPLING_RATE); - return computeOrientedBoundingBox(points); + static OrientedBoundingBox computeOrientedBoundingBox(ArrayList<GesturePoint> originalPoints) { + final int count = originalPoints.size(); + float[] points = new float[count * 2]; + for (int i = 0; i < count; i++) { + GesturePoint point = originalPoints.get(i); + int index = i * 2; + points[index] = point.x; + points[index + 1] = point.y; + } + float[] meanVector = computeCentroid(points); + return computeOrientedBoundingBox(points, meanVector); } - static OrientedBoundingBox computeOrientedBoundingBox(float[] points) { + static OrientedBoundingBox computeOrientedBoundingBox(float[] originalPoints) { + int size = originalPoints.length; + float[] points = new float[size]; + for (int i = 0; i < size; i++) { + points[i] = originalPoints[i]; + } float[] meanVector = computeCentroid(points); return computeOrientedBoundingBox(points, meanVector); } - static OrientedBoundingBox computeOrientedBoundingBox(float[] points, float[] centroid) { + private static OrientedBoundingBox computeOrientedBoundingBox(float[] points, float[] centroid) { translate(points, -centroid[0], -centroid[1]); - double[][] array = computeCoVariance(points); - double[] targetVector = computeOrientation(array); + float[][] array = computeCoVariance(points); + float[] targetVector = computeOrientation(array); float angle; if (targetVector[0] == 0 && targetVector[1] == 0) { @@ -448,25 +459,25 @@ final class GestureUtilities { return new OrientedBoundingBox((float) (angle * 180 / Math.PI), centroid[0], centroid[1], maxx - minx, maxy - miny); } - private static double[] computeOrientation(double[][] covarianceMatrix) { - double[] targetVector = new double[2]; + private static float[] computeOrientation(float[][] covarianceMatrix) { + float[] targetVector = new float[2]; if (covarianceMatrix[0][1] == 0 || covarianceMatrix[1][0] == 0) { targetVector[0] = 1; targetVector[1] = 0; } - double a = -covarianceMatrix[0][0] - covarianceMatrix[1][1]; - double b = covarianceMatrix[0][0] * covarianceMatrix[1][1] - covarianceMatrix[0][1] + float a = -covarianceMatrix[0][0] - covarianceMatrix[1][1]; + float b = covarianceMatrix[0][0] * covarianceMatrix[1][1] - covarianceMatrix[0][1] * covarianceMatrix[1][0]; - double value = a / 2; - double rightside = Math.sqrt(Math.pow(value, 2) - b); - double lambda1 = -value + rightside; - double lambda2 = -value - rightside; + float value = a / 2; + float rightside = (float) Math.sqrt(Math.pow(value, 2) - b); + float lambda1 = -value + rightside; + float lambda2 = -value - rightside; if (lambda1 == lambda2) { targetVector[0] = 0; targetVector[1] = 0; } else { - double lambda = lambda1 > lambda2 ? lambda1 : lambda2; + float lambda = lambda1 > lambda2 ? lambda1 : lambda2; targetVector[0] = 1; targetVector[1] = (lambda - covarianceMatrix[0][0]) / covarianceMatrix[0][1]; } @@ -474,13 +485,13 @@ final class GestureUtilities { } - static float[] rotate(float[] points, double angle) { - double cos = Math.cos(angle); - double sin = Math.sin(angle); + static float[] rotate(float[] points, float angle) { + float cos = (float) Math.cos(angle); + float sin = (float) Math.sin(angle); int size = points.length; for (int i = 0; i < size; i += 2) { - float x = (float) (points[i] * cos - points[i + 1] * sin); - float y = (float) (points[i] * sin + points[i + 1] * cos); + float x = points[i] * cos - points[i + 1] * sin; + float y = points[i] * sin + points[i + 1] * cos; points[i] = x; points[i + 1] = y; } diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index 7fb9daf3c560..93b5b4d3fa6f 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/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 4b9b36637dae..170477f95caa 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); @@ -3986,7 +3988,8 @@ class PackageManagerService extends IPackageManager.Stub { Log.w(TAG, "Package couldn't be installed in " + pkg.mPath); if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) { res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK; - } + } + } else { updateSettingsLI(newPackage, installerPackageName, res); |