diff --git a/core/api/current.txt b/core/api/current.txt index 51e117ed213f2..20d35a958c25c 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -32991,6 +32991,7 @@ package android.provider { field public static final String CACHED_PHOTO_URI = "photo_uri"; field public static final String CALL_SCREENING_APP_NAME = "call_screening_app_name"; field public static final String CALL_SCREENING_COMPONENT_NAME = "call_screening_component_name"; + field public static final String COMPOSER_PHOTO_URI = "composer_photo_uri"; field public static final android.net.Uri CONTENT_FILTER_URI; field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/calls"; field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/calls"; @@ -33015,6 +33016,7 @@ package android.provider { field public static final String IS_READ = "is_read"; field public static final String LAST_MODIFIED = "last_modified"; field public static final String LIMIT_PARAM_KEY = "limit"; + field public static final String LOCATION = "location"; field public static final String MISSED_REASON = "missed_reason"; field public static final long MISSED_REASON_NOT_MISSED = 0L; // 0x0L field public static final int MISSED_TYPE = 3; // 0x3 @@ -33030,7 +33032,11 @@ package android.provider { field public static final int PRESENTATION_PAYPHONE = 4; // 0x4 field public static final int PRESENTATION_RESTRICTED = 2; // 0x2 field public static final int PRESENTATION_UNKNOWN = 3; // 0x3 + field public static final String PRIORITY = "priority"; + field public static final int PRIORITY_NORMAL = 0; // 0x0 + field public static final int PRIORITY_URGENT = 1; // 0x1 field public static final int REJECTED_TYPE = 5; // 0x5 + field public static final String SUBJECT = "subject"; field public static final String TRANSCRIPTION = "transcription"; field public static final String TYPE = "type"; field public static final long USER_MISSED_CALL_FILTERS_TIMEOUT = 4194304L; // 0x400000L @@ -33045,6 +33051,15 @@ package android.provider { field public static final String VOICEMAIL_URI = "voicemail_uri"; } + public static class CallLog.Locations implements android.provider.BaseColumns { + field public static final String AUTHORITY = "call_composer_locations"; + field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/call_composer_location"; + field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/call_composer_location"; + field @NonNull public static final android.net.Uri CONTENT_URI; + field public static final String LATITUDE = "latitude"; + field public static final String LONGITUDE = "longitude"; + } + @Deprecated public class Contacts { field @Deprecated public static final String AUTHORITY = "contacts"; field @Deprecated public static final android.net.Uri CONTENT_URI; diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index 0bdd574d77ddf..33e33ee5fa021 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -24,6 +24,7 @@ import android.annotation.LongDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.ContentProvider; @@ -189,7 +190,7 @@ public class CallLog { * This method is used by Telephony to store pictures selected by the user or sent from the * remote party as part of a voice call with call composer. The {@link Uri} supplied in the * callback can be used to retrieve the image via {@link ContentResolver#openFile} or stored in - * the {@link Calls} table in the TODO: link column name. + * the {@link Calls} table in the {@link Calls#COMPOSER_PHOTO_URI} column. * * The caller is responsible for closing the {@link InputStream} after the callback indicating * success or failure. @@ -387,6 +388,320 @@ public class CallLog { cb.onError(new CallComposerLoggingException(error)); } + /** + * Used as an argument to {@link Calls#addCall(Context, AddCallParams)}. + * + * Contains details to log about a call. + * @hide + */ + public static class AddCallParams { + + /** + * Builder for the add-call parameters. + */ + public static final class AddCallParametersBuilder { + private CallerInfo mCallerInfo; + private String mNumber; + private String mPostDialDigits; + private String mViaNumber; + private int mPresentation = TelecomManager.PRESENTATION_UNKNOWN; + private int mCallType = Calls.INCOMING_TYPE; + private int mFeatures; + private PhoneAccountHandle mAccountHandle; + private long mStart; + private int mDuration; + private Long mDataUsage = Long.MIN_VALUE; + private boolean mAddForAllUsers; + private UserHandle mUserToBeInsertedTo; + private boolean mIsRead; + private int mCallBlockReason = Calls.BLOCK_REASON_NOT_BLOCKED; + private CharSequence mCallScreeningAppName; + private String mCallScreeningComponentName; + private long mMissedReason = Calls.MISSED_REASON_NOT_MISSED; + private int mPriority = Calls.PRIORITY_NORMAL; + private String mSubject; + private double mLatitude = Double.NaN; + private double mLongitude = Double.NaN; + private Uri mPictureUri; + + /** + * @param callerInfo the CallerInfo object to get the target contact from. + */ + public @NonNull AddCallParametersBuilder setCallerInfo( + @NonNull CallerInfo callerInfo) { + mCallerInfo = callerInfo; + return this; + } + + /** + * @param number the phone number to be added to the calls db + */ + public @NonNull AddCallParametersBuilder setNumber(@NonNull String number) { + mNumber = number; + return this; + } + + /** + * @param postDialDigits the post-dial digits that were dialed after the number, + * if it was outgoing. Otherwise it is ''. + */ + public @NonNull AddCallParametersBuilder setPostDialDigits( + @NonNull String postDialDigits) { + mPostDialDigits = postDialDigits; + return this; + } + + /** + * @param viaNumber the secondary number that the incoming call received with. If the + * call was received with the SIM assigned number, then this field must be ''. + */ + public @NonNull AddCallParametersBuilder setViaNumber(@NonNull String viaNumber) { + mViaNumber = viaNumber; + return this; + } + + /** + * @param presentation enum value from TelecomManager.PRESENTATION_xxx, which + * is set by the network and denotes the number presenting rules for + * "allowed", "payphone", "restricted" or "unknown" + */ + public @NonNull AddCallParametersBuilder setPresentation(int presentation) { + mPresentation = presentation; + return this; + } + + /** + * @param callType enumerated values for "incoming", "outgoing", or "missed" + */ + public @NonNull AddCallParametersBuilder setCallType(int callType) { + mCallType = callType; + return this; + } + + /** + * @param features features of the call (e.g. Video). + */ + public @NonNull AddCallParametersBuilder setFeatures(int features) { + mFeatures = features; + return this; + } + + /** + * @param accountHandle The accountHandle object identifying the provider of the call + */ + public @NonNull AddCallParametersBuilder setAccountHandle( + @NonNull PhoneAccountHandle accountHandle) { + mAccountHandle = accountHandle; + return this; + } + + /** + * @param start time stamp for the call in milliseconds + */ + public @NonNull AddCallParametersBuilder setStart(long start) { + mStart = start; + return this; + } + + /** + * @param duration call duration in seconds + */ + public @NonNull AddCallParametersBuilder setDuration(int duration) { + mDuration = duration; + return this; + } + + /** + * @param dataUsage data usage for the call in bytes or + * {@link Long#MIN_VALUE} if data usage was not tracked + * for the call. + */ + public @NonNull AddCallParametersBuilder setDataUsage(long dataUsage) { + mDataUsage = dataUsage; + return this; + } + + /** + * @param addForAllUsers If true, the call is added to the call log of all currently + * running users. The caller must have the MANAGE_USERS permission if this is + * true. + */ + public @NonNull AddCallParametersBuilder setAddForAllUsers( + boolean addForAllUsers) { + mAddForAllUsers = addForAllUsers; + return this; + } + + /** + * @param userToBeInsertedTo {@link UserHandle} of user that the call is going to be + * inserted to. null if it is inserted to the current user. + * The value is ignored if {@link #setAddForAllUsers} is + * called with {@code true}. + */ + @SuppressLint("UserHandleName") + public @NonNull AddCallParametersBuilder setUserToBeInsertedTo( + @NonNull UserHandle userToBeInsertedTo) { + mUserToBeInsertedTo = userToBeInsertedTo; + return this; + } + + /** + * @param isRead Flag to show if the missed call log has been read by the user or not. + * Used for call log restore of missed calls. + */ + public @NonNull AddCallParametersBuilder setIsRead(boolean isRead) { + mIsRead = isRead; + return this; + } + + /** + * @param callBlockReason The reason why the call is blocked. + */ + public @NonNull AddCallParametersBuilder setCallBlockReason(int callBlockReason) { + mCallBlockReason = callBlockReason; + return this; + } + + /** + * @param callScreeningAppName The call screening application name which block the call. + */ + public @NonNull AddCallParametersBuilder setCallScreeningAppName( + @NonNull CharSequence callScreeningAppName) { + mCallScreeningAppName = callScreeningAppName; + return this; + } + + /** + * @param callScreeningComponentName The call screening component name which blocked + * the call. + */ + public @NonNull AddCallParametersBuilder setCallScreeningComponentName( + @NonNull String callScreeningComponentName) { + mCallScreeningComponentName = callScreeningComponentName; + return this; + } + + /** + * @param missedReason The encoded missed information of the call. + */ + public @NonNull AddCallParametersBuilder setMissedReason(long missedReason) { + mMissedReason = missedReason; + return this; + } + + /** + * @param priority The priority of the call, either {@link Calls#PRIORITY_NORMAL} + * or {@link Calls#PRIORITY_URGENT} as sent via call composer + */ + public @NonNull AddCallParametersBuilder setPriority(int priority) { + mPriority = priority; + return this; + } + + /** + * @param subject The subject as sent via call composer. + */ + public @NonNull AddCallParametersBuilder setSubject(@NonNull String subject) { + mSubject = subject; + return this; + } + + /** + * @param latitude Latitude of the location sent via call composer. + */ + public @NonNull AddCallParametersBuilder setLatitude(double latitude) { + mLatitude = latitude; + return this; + } + + /** + * @param longitude Longitude of the location sent via call composer. + */ + public @NonNull AddCallParametersBuilder setLongitude(double longitude) { + mLongitude = longitude; + return this; + } + + /** + * @param pictureUri {@link Uri} returned from {@link #storeCallComposerPictureAsUser}. + * Associates that stored picture with this call in the log. + */ + public @NonNull AddCallParametersBuilder setPictureUri(@NonNull Uri pictureUri) { + mPictureUri = pictureUri; + return this; + } + + /** + * Builds the object + */ + public @NonNull AddCallParams build() { + return new AddCallParams(mCallerInfo, mNumber, mPostDialDigits, mViaNumber, + mPresentation, mCallType, mFeatures, mAccountHandle, mStart, mDuration, + mDataUsage, mAddForAllUsers, mUserToBeInsertedTo, mIsRead, mCallBlockReason, + mCallScreeningAppName, mCallScreeningComponentName, mMissedReason, + mPriority, mSubject, mLatitude, mLongitude, mPictureUri); + } + } + + private CallerInfo mCallerInfo; + private String mNumber; + private String mPostDialDigits; + private String mViaNumber; + private int mPresentation; + private int mCallType; + private int mFeatures; + private PhoneAccountHandle mAccountHandle; + private long mStart; + private int mDuration; + private long mDataUsage; + private boolean mAddForAllUsers; + private UserHandle mUserToBeInsertedTo; + private boolean mIsRead; + private int mCallBlockReason; + private CharSequence mCallScreeningAppName; + private String mCallScreeningComponentName; + private long mMissedReason; + private int mPriority; + private String mSubject; + private double mLatitude = Double.NaN; + private double mLongitude = Double.NaN; + private Uri mPictureUri; + + private AddCallParams(CallerInfo callerInfo, String number, String postDialDigits, + String viaNumber, int presentation, int callType, int features, + PhoneAccountHandle accountHandle, long start, int duration, long dataUsage, + boolean addForAllUsers, UserHandle userToBeInsertedTo, boolean isRead, + int callBlockReason, + CharSequence callScreeningAppName, String callScreeningComponentName, + long missedReason, + int priority, String subject, double latitude, double longitude, Uri pictureUri) { + mCallerInfo = callerInfo; + mNumber = number; + mPostDialDigits = postDialDigits; + mViaNumber = viaNumber; + mPresentation = presentation; + mCallType = callType; + mFeatures = features; + mAccountHandle = accountHandle; + mStart = start; + mDuration = duration; + mDataUsage = dataUsage; + mAddForAllUsers = addForAllUsers; + mUserToBeInsertedTo = userToBeInsertedTo; + mIsRead = isRead; + mCallBlockReason = callBlockReason; + mCallScreeningAppName = callScreeningAppName; + mCallScreeningComponentName = callScreeningComponentName; + mMissedReason = missedReason; + mPriority = priority; + mSubject = subject; + mLatitude = latitude; + mLongitude = longitude; + mPictureUri = pictureUri; + } + + } + /** * Contains the recent calls. */ @@ -1065,6 +1380,83 @@ public class CallLog { */ public static final String MISSED_REASON = "missed_reason"; + /** + * The subject of the call, as delivered via call composer. + * + * For outgoing calls, contains the subject set by the local user. For incoming calls, + * contains the subject set by the remote caller. May be null if no subject was set. + *

Type: TEXT

+ */ + public static final String SUBJECT = "subject"; + + /** + * Used as a value in the {@link #PRIORITY} column. + * + * Indicates that the call is of normal priority. This is also the default value for calls + * that did not include call composer elements. + */ + public static final int PRIORITY_NORMAL = 0; + + /** + * Used as a value in the {@link #PRIORITY} column. + * + * Indicates that the call is of urgent priority. + */ + public static final int PRIORITY_URGENT = 1; + + /** + * The priority of the call, as delivered via call composer. + * + * For outgoing calls, contains the priority set by the local user. For incoming calls, + * contains the priority set by the remote caller. If no priority was set or the call + * did not include call composer elements, defaults to {@link #PRIORITY_NORMAL}. + * Valid values are {@link #PRIORITY_NORMAL} and {@link #PRIORITY_URGENT}. + *

Type: INTEGER

+ */ + public static final String PRIORITY = "priority"; + + /** + * A reference to the picture that was sent via call composer. + * + * The string contained in this field should be converted to an {@link Uri} via + * {@link Uri#parse(String)}, then passed to {@link ContentResolver#openFileDescriptor} + * in order to obtain a file descriptor to access the picture data. + * + * The user may choose to delete the picture associated with a call independently of the + * call log entry, in which case {@link ContentResolver#openFileDescriptor} may throw a + * {@link FileNotFoundException}. + * + * Note that pictures sent or received via call composer will not be included in any + * backups of the call log. + * + *

Type: TEXT

+ */ + public static final String COMPOSER_PHOTO_URI = "composer_photo_uri"; + + /** + * A reference to the location that was sent via call composer. + * + * This column contains the content URI of the corresponding entry in {@link Locations} + * table, which contains the actual location data. The + * {@link Manifest.permission#ACCESS_FINE_LOCATION} permission is required to access that + * table. + * + * If your app has the appropriate permissions, the location data may be obtained by + * converting the value of this column to an {@link Uri} via {@link Uri#parse}, then passing + * the result to {@link ContentResolver#query}. + * + * The user may choose to delete the location associated with a call independently of the + * call log entry, in which case the {@link Cursor} returned from + * {@link ContentResolver#query} will either be {@code null} or empty, with + * {@link Cursor#getCount()} returning {@code 0}. + * + * This column will not be populated when a call is received while the device is locked, and + * it will not be part of any backups. + * + *

Type: TEXT

+ */ + public static final String LOCATION = "location"; + /** * Adds a call to the call log. * @@ -1139,6 +1531,7 @@ public class CallLog { null /* callScreeningComponentName */, missedReason); } + /** * Adds a call to the call log. * @@ -1184,19 +1577,52 @@ public class CallLog { Long dataUsage, boolean addForAllUsers, UserHandle userToBeInsertedTo, boolean isRead, int callBlockReason, CharSequence callScreeningAppName, String callScreeningComponentName, long missedReason) { + AddCallParams.AddCallParametersBuilder builder = + new AddCallParams.AddCallParametersBuilder(); + builder.setCallerInfo(ci); + builder.setNumber(number); + builder.setPostDialDigits(postDialDigits); + builder.setViaNumber(viaNumber); + builder.setPresentation(presentation); + builder.setCallType(callType); + builder.setFeatures(features); + builder.setAccountHandle(accountHandle); + builder.setStart(start); + builder.setDuration(duration); + builder.setDataUsage(dataUsage == null ? Long.MIN_VALUE : dataUsage); + builder.setAddForAllUsers(addForAllUsers); + builder.setUserToBeInsertedTo(userToBeInsertedTo); + builder.setIsRead(isRead); + builder.setCallBlockReason(callBlockReason); + builder.setCallScreeningAppName(callScreeningAppName); + builder.setCallScreeningComponentName(callScreeningComponentName); + builder.setMissedReason(missedReason); + + return addCall(context, builder.build()); + } + + /** + * Adds a call to the call log, using the provided parameters + * @result The URI of the call log entry belonging to the user that made or received this + * call. This could be of the shadow provider. Do not return it to non-system apps, + * as they don't have permissions. + * @hide + */ + public static @NonNull Uri addCall( + @NonNull Context context, @NonNull AddCallParams params) { if (VERBOSE_LOG) { Log.v(LOG_TAG, String.format("Add call: number=%s, user=%s, for all=%s", - number, userToBeInsertedTo, addForAllUsers)); + params.mNumber, params.mUserToBeInsertedTo, params.mAddForAllUsers)); } final ContentResolver resolver = context.getContentResolver(); - String accountAddress = getLogAccountAddress(context, accountHandle); + String accountAddress = getLogAccountAddress(context, params.mAccountHandle); - int numberPresentation = getLogNumberPresentation(number, presentation); - String name = (ci != null) ? ci.getName() : ""; + int numberPresentation = getLogNumberPresentation(params.mNumber, params.mPresentation); + String name = (params.mCallerInfo != null) ? params.mCallerInfo.getName() : ""; if (numberPresentation != PRESENTATION_ALLOWED) { - number = ""; - if (ci != null) { + params.mNumber = ""; + if (params.mCallerInfo != null) { name = ""; } } @@ -1204,41 +1630,46 @@ public class CallLog { // accountHandle information String accountComponentString = null; String accountId = null; - if (accountHandle != null) { - accountComponentString = accountHandle.getComponentName().flattenToString(); - accountId = accountHandle.getId(); + if (params.mAccountHandle != null) { + accountComponentString = params.mAccountHandle.getComponentName().flattenToString(); + accountId = params.mAccountHandle.getId(); } - ContentValues values = new ContentValues(6); + ContentValues values = new ContentValues(14); - values.put(NUMBER, number); - values.put(POST_DIAL_DIGITS, postDialDigits); - values.put(VIA_NUMBER, viaNumber); + values.put(NUMBER, params.mNumber); + values.put(POST_DIAL_DIGITS, params.mPostDialDigits); + values.put(VIA_NUMBER, params.mViaNumber); values.put(NUMBER_PRESENTATION, Integer.valueOf(numberPresentation)); - values.put(TYPE, Integer.valueOf(callType)); - values.put(FEATURES, features); - values.put(DATE, Long.valueOf(start)); - values.put(DURATION, Long.valueOf(duration)); - if (dataUsage != null) { - values.put(DATA_USAGE, dataUsage); + values.put(TYPE, Integer.valueOf(params.mCallType)); + values.put(FEATURES, params.mFeatures); + values.put(DATE, Long.valueOf(params.mStart)); + values.put(DURATION, Long.valueOf(params.mDuration)); + if (params.mDataUsage != Long.MIN_VALUE) { + values.put(DATA_USAGE, params.mDataUsage); } values.put(PHONE_ACCOUNT_COMPONENT_NAME, accountComponentString); values.put(PHONE_ACCOUNT_ID, accountId); values.put(PHONE_ACCOUNT_ADDRESS, accountAddress); values.put(NEW, Integer.valueOf(1)); values.put(CACHED_NAME, name); - values.put(ADD_FOR_ALL_USERS, addForAllUsers ? 1 : 0); + values.put(ADD_FOR_ALL_USERS, params.mAddForAllUsers ? 1 : 0); - if (callType == MISSED_TYPE) { - values.put(IS_READ, Integer.valueOf(isRead ? 1 : 0)); + if (params.mCallType == MISSED_TYPE) { + values.put(IS_READ, Integer.valueOf(params.mIsRead ? 1 : 0)); } - values.put(BLOCK_REASON, callBlockReason); - values.put(CALL_SCREENING_APP_NAME, charSequenceToString(callScreeningAppName)); - values.put(CALL_SCREENING_COMPONENT_NAME, callScreeningComponentName); - values.put(MISSED_REASON, Long.valueOf(missedReason)); + values.put(BLOCK_REASON, params.mCallBlockReason); + values.put(CALL_SCREENING_APP_NAME, charSequenceToString(params.mCallScreeningAppName)); + values.put(CALL_SCREENING_COMPONENT_NAME, params.mCallScreeningComponentName); + values.put(MISSED_REASON, Long.valueOf(params.mMissedReason)); + values.put(PRIORITY, params.mPriority); + values.put(SUBJECT, params.mSubject); + if (params.mPictureUri != null) { + values.put(COMPOSER_PHOTO_URI, params.mPictureUri.toString()); + } - if ((ci != null) && (ci.getContactId() > 0)) { + if ((params.mCallerInfo != null) && (params.mCallerInfo.getContactId() > 0)) { // Update usage information for the number associated with the contact ID. // We need to use both the number and the ID for obtaining a data ID since other // contacts may have the same number. @@ -1247,23 +1678,23 @@ public class CallLog { // We should prefer normalized one (probably coming from // Phone.NORMALIZED_NUMBER column) first. If it isn't available try others. - if (ci.normalizedNumber != null) { - final String normalizedPhoneNumber = ci.normalizedNumber; + if (params.mCallerInfo.normalizedNumber != null) { + final String normalizedPhoneNumber = params.mCallerInfo.normalizedNumber; cursor = resolver.query(Phone.CONTENT_URI, new String[] { Phone._ID }, Phone.CONTACT_ID + " =? AND " + Phone.NORMALIZED_NUMBER + " =?", - new String[] { String.valueOf(ci.getContactId()), + new String[] { String.valueOf(params.mCallerInfo.getContactId()), normalizedPhoneNumber}, null); } else { - final String phoneNumber = ci.getPhoneNumber() != null - ? ci.getPhoneNumber() : number; + final String phoneNumber = params.mCallerInfo.getPhoneNumber() != null + ? params.mCallerInfo.getPhoneNumber() : params.mNumber; cursor = resolver.query( Uri.withAppendedPath(Callable.CONTENT_FILTER_URI, Uri.encode(phoneNumber)), new String[] { Phone._ID }, Phone.CONTACT_ID + " =?", - new String[] { String.valueOf(ci.getContactId()) }, + new String[] { String.valueOf(params.mCallerInfo.getContactId()) }, null); } @@ -1272,10 +1703,10 @@ public class CallLog { if (cursor.getCount() > 0 && cursor.moveToFirst()) { final String dataId = cursor.getString(0); updateDataUsageStatForData(resolver, dataId); - if (duration >= MIN_DURATION_FOR_NORMALIZED_NUMBER_UPDATE_MS - && callType == Calls.OUTGOING_TYPE - && TextUtils.isEmpty(ci.normalizedNumber)) { - updateNormalizedNumber(context, resolver, dataId, number); + if (params.mDuration >= MIN_DURATION_FOR_NORMALIZED_NUMBER_UPDATE_MS + && params.mCallType == Calls.OUTGOING_TYPE + && TextUtils.isEmpty(params.mCallerInfo.normalizedNumber)) { + updateNormalizedNumber(context, resolver, dataId, params.mNumber); } } } finally { @@ -1311,7 +1742,18 @@ public class CallLog { final UserManager userManager = context.getSystemService(UserManager.class); final int currentUserId = userManager.getUserHandle(); - if (addForAllUsers) { + if (params.mAddForAllUsers) { + if (userManager.isUserUnlocked(UserHandle.SYSTEM)) { + // If the user is unlocked, insert to the location provider if a location is + // provided. Do not store location if the device is still locked -- this + // puts it into device-encrypted storage instead of credential-encrypted + // storage. + Uri locationUri = maybeInsertLocation(params, resolver, UserHandle.SYSTEM); + if (locationUri != null) { + values.put(Calls.LOCATION, locationUri.toString()); + } + } + // First, insert to the system user. final Uri uriForSystem = addEntryAndRemoveExpiredEntries( context, userManager, UserHandle.SYSTEM, values); @@ -1351,6 +1793,12 @@ public class CallLog { // start. if (userManager.isUserRunning(userHandle) && userManager.isUserUnlocked(userHandle)) { + Uri locationUri = maybeInsertLocation(params, resolver, userHandle); + if (locationUri != null) { + values.put(Calls.LOCATION, locationUri.toString()); + } else { + values.put(Calls.LOCATION, (String) null); + } final Uri uri = addEntryAndRemoveExpiredEntries(context, userManager, userHandle, values); if (userId == currentUserId) { @@ -1361,10 +1809,20 @@ public class CallLog { } else { // Single-user entry. Just write to that user, assuming it's running. If the // user is encrypted, we write to the shadow calllog. - - final UserHandle targetUserHandle = userToBeInsertedTo != null - ? userToBeInsertedTo + final UserHandle targetUserHandle = params.mUserToBeInsertedTo != null + ? params.mUserToBeInsertedTo : UserHandle.of(currentUserId); + + if (userManager.isUserRunning(targetUserHandle) + && userManager.isUserUnlocked(targetUserHandle)) { + Uri locationUri = maybeInsertLocation(params, resolver, targetUserHandle); + if (locationUri != null) { + values.put(Calls.LOCATION, locationUri.toString()); + } else { + values.put(Calls.LOCATION, (String) null); + } + } + result = addEntryAndRemoveExpiredEntries(context, userManager, targetUserHandle, values); } @@ -1481,6 +1939,27 @@ public class CallLog { } } + private static Uri maybeInsertLocation(AddCallParams params, ContentResolver resolver, + UserHandle user) { + if (Double.isNaN(params.mLatitude) || Double.isNaN(params.mLongitude)) { + return null; + } + ContentValues locationValues = new ContentValues(); + locationValues.put(Locations.LATITUDE, params.mLatitude); + locationValues.put(Locations.LONGITUDE, params.mLongitude); + Uri locationUri = ContentProvider.maybeAddUserId(Locations.CONTENT_URI, + user.getIdentifier()); + try { + return resolver.insert(locationUri, locationValues); + } catch (SecurityException e) { + // This can happen if the caller doesn't have location permissions. If that's the + // case just skip the insertion. + Log.w(LOG_TAG, "Skipping inserting location because caller lacks" + + " ACCESS_FINE_LOCATION."); + return null; + } + } + private static void updateDataUsageStatForData(ContentResolver resolver, String dataId) { final Uri feedbackUri = DataUsageFeedback.FEEDBACK_URI.buildUpon() .appendPath(dataId) @@ -1587,4 +2066,47 @@ public class CallLog { return missedReason >= (USER_MISSED_NO_ANSWER); } } + + /** + * Table that contains information on location data sent via call composer. + * + * All fields in this table require the {@link Manifest.permission#ACCESS_FINE_LOCATION} + * permission for access. + */ + public static class Locations implements BaseColumns { + private Locations() {} + /** + * Authority for the locations content provider. + */ + public static final String AUTHORITY = "call_composer_locations"; + + /** + * Content type for the location table. + */ + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/call_composer_location"; + + /** + * Content type for the location entries. + */ + public static final String CONTENT_ITEM_TYPE = + "vnd.android.cursor.item/call_composer_location"; + + /** + * The content URI for this table + */ + @NonNull + public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY); + + /** + * Latitude in degrees. See {@link android.location.Location#setLatitude(double)}. + *

Type: REAL

+ */ + public static final String LATITUDE = "latitude"; + + /** + * Longitude in degrees. See {@link android.location.Location#setLongitude(double)}. + *

Type: REAL

+ */ + public static final String LONGITUDE = "longitude"; + } } diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index eb5e0a251b64c..c3d822cce08dc 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -462,6 +462,8 @@ applications that come with the platform + + diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 41cc835ead825..5c431071fc7f3 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -387,6 +387,9 @@ + + +