diff options
| author | 2018-12-12 19:07:05 +0000 | |
|---|---|---|
| committer | 2018-12-12 19:07:05 +0000 | |
| commit | 3dc2f1ef5963695131f8524ff52a8a3a4ecb124c (patch) | |
| tree | 1434c54e9f0036ff2ebe346540a9cc2cc336d78d | |
| parent | 85f7df624311ce4eb53b79fda3cc53551cce73d3 (diff) | |
| parent | 369f96dd980ac5ee9690c82b82977f1a55576b9e (diff) | |
Merge "Add API to view calendar events cross profile."
6 files changed, 164 insertions, 14 deletions
diff --git a/api/current.txt b/api/current.txt index 5d208e322ff3..3b9ac8a7f0be 100644 --- a/api/current.txt +++ b/api/current.txt @@ -35966,9 +35966,11 @@ package android.provider { } public final class CalendarContract { + method public static boolean startViewCalendarEventInManagedProfile(android.content.Context, long, long, long, boolean, int); field public static final java.lang.String ACCOUNT_TYPE_LOCAL = "LOCAL"; field public static final java.lang.String ACTION_EVENT_REMINDER = "android.intent.action.EVENT_REMINDER"; field public static final java.lang.String ACTION_HANDLE_CUSTOM_EVENT = "android.provider.calendar.action.HANDLE_CUSTOM_EVENT"; + field public static final java.lang.String ACTION_VIEW_WORK_CALENDAR_EVENT = "android.provider.calendar.action.VIEW_WORK_CALENDAR_EVENT"; field public static final java.lang.String AUTHORITY = "com.android.calendar"; field public static final java.lang.String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter"; field public static final android.net.Uri CONTENT_URI; @@ -35976,6 +35978,7 @@ package android.provider { field public static final java.lang.String EXTRA_EVENT_ALL_DAY = "allDay"; field public static final java.lang.String EXTRA_EVENT_BEGIN_TIME = "beginTime"; field public static final java.lang.String EXTRA_EVENT_END_TIME = "endTime"; + field public static final java.lang.String EXTRA_EVENT_ID = "id"; } public static final class CalendarContract.Attendees implements android.provider.BaseColumns android.provider.CalendarContract.AttendeesColumns android.provider.CalendarContract.EventsColumns { diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 5462f5e33af3..5ef3c4ceaf09 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -66,6 +66,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.UserManager.UserOperationException; import android.os.UserManager.UserOperationResult; +import android.provider.CalendarContract; import android.provider.ContactsContract.Directory; import android.provider.Settings; import android.security.AttestedKeyPair; @@ -10347,4 +10348,33 @@ public class DevicePolicyManager { } return false; } + + /** + * Starts an activity to view calendar events in the managed profile. + * + * @param eventId the id of the event to be viewed. + * @param start the start time of the event. + * @param end the end time of the event. + * @param allDay if the event is an all-day event. + * @param flags flags to be set for the intent + * @return {@code true} if the activity is started successfully. {@code false} otherwise. + * + * @see CalendarContract#startViewCalenderEventInManagedProfile(Context, String, long, long, + * long, boolean, int) + * + * @hide + */ + public boolean startViewCalendarEventInManagedProfile(long eventId, long start, long end, + boolean allDay, int flags) { + throwIfParentInstance("startViewCalendarEventInManagedProfile"); + if (mService != null) { + try { + return mService.startViewCalendarEventInManagedProfile(mContext.getPackageName(), + eventId, start, end, allDay, flags); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return false; + } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 538ee8925cd5..74cb22c3e645 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -431,4 +431,6 @@ interface IDevicePolicyManager { boolean isManagedKiosk(); boolean isUnattendedManagedKiosk(); + + boolean startViewCalendarEventInManagedProfile(String packageName, long eventId, long start, long end, boolean allDay, int flags); } diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java index 865b8f8482bd..c167ea18f0c5 100644 --- a/core/java/android/provider/CalendarContract.java +++ b/core/java/android/provider/CalendarContract.java @@ -16,6 +16,7 @@ package android.provider; +import android.annotation.NonNull; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.UnsupportedAppUsage; @@ -41,6 +42,8 @@ import android.text.format.DateUtils; import android.text.format.Time; import android.util.Log; +import com.android.internal.util.Preconditions; + /** * <p> * The contract between the calendar provider and applications. Contains @@ -129,6 +132,13 @@ public final class CalendarContract { "android.provider.calendar.action.HANDLE_CUSTOM_EVENT"; /** + * Action used to help apps show calendar events in the managed profile. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_VIEW_WORK_CALENDAR_EVENT = + "android.provider.calendar.action.VIEW_WORK_CALENDAR_EVENT"; + + /** * Intent Extras key: {@link EventsColumns#CUSTOM_APP_URI} for the event in * the {@link #ACTION_HANDLE_CUSTOM_EVENT} intent */ @@ -153,6 +163,11 @@ public final class CalendarContract { public static final String EXTRA_EVENT_ALL_DAY = "allDay"; /** + * Intent Extras key: The id of an event. + */ + public static final String EXTRA_EVENT_ID = "id"; + + /** * This authority is used for writing to or querying from the calendar * provider. Note: This is set at first run and cannot be changed without * breaking apps that access the provider. @@ -195,6 +210,43 @@ public final class CalendarContract { private CalendarContract() {} /** + * Starts an activity to view calendar events in the managed profile. + * + * When this API is called, the system will attempt to start an activity + * in the managed profile with an intent targeting the same caller package. + * The intent will have its action set to + * {@link CalendarContract#ACTION_VIEW_WORK_CALENDAR_EVENT} and contain extras + * corresponding to the API's arguments. A calendar app intending to support + * cross profile events viewing should handle this intent, parse the arguments + * and show the appropriate UI. + * + * @param context the context. + * @param eventId the id of the event to be viewed. Will be put into {@link #EXTRA_EVENT_ID} + * field of the intent. + * @param start the start time of the event. Will be put into {@link #EXTRA_EVENT_BEGIN_TIME} + * field of the intent. + * @param end the end time of the event. Will be put into {@link #EXTRA_EVENT_END_TIME} field + * of the intent. + * @param allDay if the event is an all-day event. Will be put into + * {@link #EXTRA_EVENT_ALL_DAY} field of the intent. + * @param flags flags to be set on the intent via {@link Intent#setFlags} + * @return {@code true} if the activity is started successfully. {@code false} otherwise. + * + * @see #EXTRA_EVENT_ID + * @see #EXTRA_EVENT_BEGIN_TIME + * @see #EXTRA_EVENT_END_TIME + * @see #EXTRA_EVENT_ALL_DAY + */ + public static boolean startViewCalendarEventInManagedProfile(@NonNull Context context, + long eventId, long start, long end, boolean allDay, int flags) { + Preconditions.checkNotNull(context, "Context is null"); + final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService( + Context.DEVICE_POLICY_SERVICE); + return dpm.startViewCalendarEventInManagedProfile(eventId, start, + end, allDay, flags); + } + + /** * Generic columns for use by sync adapters. The specific functions of these * columns are private to the sync adapter. Other clients of the API should * not attempt to either read or write this column. These columns are @@ -695,7 +747,7 @@ public final class CalendarContract { public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/calendars"); /** - * The content:// style URL for querying Calendars table in the work profile. Appending a + * The content:// style URL for querying Calendars table in the managed profile. Appending a * calendar id using {@link ContentUris#withAppendedId(Uri, long)} will * specify a single calendar. * @@ -715,9 +767,9 @@ public final class CalendarContract { * projection of the query to this uri that are not contained in the above list. * * <p>This uri will return an empty cursor if the calling user is not a parent profile - * of a work profile, or cross profile calendar is disabled in Settings, or this uri is - * queried from a package that is not whitelisted by profile owner of the work profile via - * {@link DevicePolicyManager#addCrossProfileCalendarPackage(ComponentName, String)}. + * of a managed profile, or cross profile calendar is disabled in Settings, or this uri is + * queried from a package that is not whitelisted by profile owner of the managed profile + * via {@link DevicePolicyManager#addCrossProfileCalendarPackage(ComponentName, String)}. * * @see DevicePolicyManager#getCrossProfileCalendarPackages(ComponentName) * @see Settings.Secure#CROSS_PROFILE_CALENDAR_ENABLED @@ -1673,7 +1725,7 @@ public final class CalendarContract { Uri.parse("content://" + AUTHORITY + "/events"); /** - * The content:// style URL for querying Events table in the work profile. Appending an + * The content:// style URL for querying Events table in the managed profile. Appending an * event id using {@link ContentUris#withAppendedId(Uri, long)} will * specify a single event. * @@ -1706,9 +1758,9 @@ public final class CalendarContract { * projection of the query to this uri that are not contained in the above list. * * <p>This uri will return an empty cursor if the calling user is not a parent profile - * of a work profile, or cross profile calendar is disabled in Settings, or this uri is - * queried from a package that is not whitelisted by profile owner of the work profile via - * {@link DevicePolicyManager#addCrossProfileCalendarPackage(ComponentName, String)}. + * of a managed profile, or cross profile calendar is disabled in Settings, or this uri is + * queried from a package that is not whitelisted by profile owner of the managed profile + * via {@link DevicePolicyManager#addCrossProfileCalendarPackage(ComponentName, String)}. * * @see DevicePolicyManager#getCrossProfileCalendarPackages(ComponentName) * @see Settings.Secure#CROSS_PROFILE_CALENDAR_ENABLED @@ -1896,7 +1948,7 @@ public final class CalendarContract { Uri.parse("content://" + AUTHORITY + "/instances/searchbyday"); /** - * The content:// style URL for querying an instance range in the work profile. + * The content:// style URL for querying an instance range in the managed profile. * It supports similar semantics as {@link #CONTENT_URI}. * * <p>The following columns plus the columns that are whitelisted by @@ -1916,9 +1968,9 @@ public final class CalendarContract { * projection of the query to this uri that are not contained in the above list. * * <p>This uri will return an empty cursor if the calling user is not a parent profile - * of a work profile, or cross profile calendar for the work profile is disabled in + * of a managed profile, or cross profile calendar for the managed profile is disabled in * Settings, or this uri is queried from a package that is not whitelisted by - * profile owner of the work profile via + * profile owner of the managed profile via * {@link DevicePolicyManager#addCrossProfileCalendarPackage(ComponentName, String)}. * * @see DevicePolicyManager#getCrossProfileCalendarPackages(ComponentName) @@ -1929,7 +1981,7 @@ public final class CalendarContract { /** * The content:// style URL for querying an instance range by Julian - * Day in the work profile. It supports similar semantics as {@link #CONTENT_BY_DAY_URI} + * Day in the managed profile. It supports similar semantics as {@link #CONTENT_BY_DAY_URI} * and performs similar checks as {@link #ENTERPRISE_CONTENT_URI}. */ public static final Uri ENTERPRISE_CONTENT_BY_DAY_URI = @@ -1937,7 +1989,7 @@ public final class CalendarContract { /** * The content:// style URL for querying an instance range with a search - * term in the work profile. It supports similar semantics as {@link #CONTENT_SEARCH_URI} + * term in the managed profile. It supports similar semantics as {@link #CONTENT_SEARCH_URI} * and performs similar checks as {@link #ENTERPRISE_CONTENT_URI}. */ public static final Uri ENTERPRISE_CONTENT_SEARCH_URI = @@ -1945,7 +1997,7 @@ public final class CalendarContract { /** * The content:// style URL for querying an instance range with a search - * term in the work profile. It supports similar semantics as + * term in the managed profile. It supports similar semantics as * {@link #CONTENT_SEARCH_BY_DAY_URI} and performs similar checks as * {@link #ENTERPRISE_CONTENT_URI}. */ diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index 240b8206baf6..da0a9fbe44f9 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -136,4 +136,10 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { public boolean isUnattendedManagedKiosk() { return false; } + + @Override + public boolean startViewCalendarEventInManagedProfile(String packageName, long eventId, + long start, long end, boolean allDay, int flags) { + return false; + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 1166bae20f91..ce540025ca09 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -127,6 +127,7 @@ import android.app.admin.SystemUpdatePolicy; import android.app.backup.IBackupManager; import android.app.trust.TrustManager; import android.app.usage.UsageStatsManagerInternal; +import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentValues; @@ -187,6 +188,7 @@ import android.os.UserManager; import android.os.UserManagerInternal; import android.os.UserManagerInternal.UserRestrictionsListener; import android.os.storage.StorageManager; +import android.provider.CalendarContract; import android.provider.ContactsContract.QuickContact; import android.provider.ContactsInternal; import android.provider.Settings; @@ -13797,4 +13799,59 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private PowerManagerInternal getPowerManagerInternal() { return mInjector.getPowerManagerInternal(); } + + @Override + public boolean startViewCalendarEventInManagedProfile(String packageName, long eventId, + long start, long end, boolean allDay, int flags) { + if (!mHasFeature) { + return false; + } + Preconditions.checkStringNotEmpty(packageName, "Package name is empty"); + + final int callingUid = mInjector.binderGetCallingUid(); + final int callingUserId = mInjector.userHandleGetCallingUserId(); + if (!isCallingFromPackage(packageName, callingUid)) { + throw new SecurityException("Input package name doesn't align with actual " + + "calling package."); + } + final long identity = mInjector.binderClearCallingIdentity(); + try { + final int workProfileUserId = getManagedUserId(callingUserId); + if (workProfileUserId < 0) { + return false; + } + if (!isPackageAllowedToAccessCalendarForUser(packageName, workProfileUserId)) { + Log.d(LOG_TAG, String.format("Package %s is not allowed to access cross-profile" + + "calendar APIs", packageName)); + return false; + } + final Intent intent = new Intent(CalendarContract.ACTION_VIEW_WORK_CALENDAR_EVENT); + intent.setPackage(packageName); + intent.putExtra(CalendarContract.EXTRA_EVENT_ID, eventId); + intent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, start); + intent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, end); + intent.putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, allDay); + intent.setFlags(flags); + try { + mContext.startActivityAsUser(intent, UserHandle.of(workProfileUserId)); + } catch (ActivityNotFoundException e) { + Log.e(LOG_TAG, "View event activity not found", e); + return false; + } + } finally { + mInjector.binderRestoreCallingIdentity(identity); + } + return true; + } + + private boolean isCallingFromPackage(String packageName, int callingUid) { + try { + final int packageUid = mInjector.getPackageManager().getPackageUidAsUser( + packageName, UserHandle.getUserId(callingUid)); + return packageUid == callingUid; + } catch (NameNotFoundException e) { + Log.d(LOG_TAG, "Calling package not found", e); + return false; + } + } } |