diff options
author | John Spurlock <jspurlock@google.com> | 2015-05-26 19:41:51 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2015-05-26 19:41:53 +0000 |
commit | 12e0b390efd2fb8c21a2700be9dcd95d15b31ba6 (patch) | |
tree | 333109ed658973b1ec9447bd793e2be6de73c931 /services/core | |
parent | c2ddb3e452d4d3a360ad697bf89a786f2af7a377 (diff) | |
parent | 1b8b22b1a412539020f78a132cff7c8fa7fae258 (diff) | |
download | frameworks_base-12e0b390efd2fb8c21a2700be9dcd95d15b31ba6.zip frameworks_base-12e0b390efd2fb8c21a2700be9dcd95d15b31ba6.tar.gz frameworks_base-12e0b390efd2fb8c21a2700be9dcd95d15b31ba6.tar.bz2 |
Merge "Zen: Calendar auto-rule tracks primary calendar for corp profile." into mnc-dev
Diffstat (limited to 'services/core')
9 files changed, 192 insertions, 52 deletions
diff --git a/services/core/java/com/android/server/notification/CalendarTracker.java b/services/core/java/com/android/server/notification/CalendarTracker.java index 71d7f19..de321fe 100644 --- a/services/core/java/com/android/server/notification/CalendarTracker.java +++ b/services/core/java/com/android/server/notification/CalendarTracker.java @@ -26,8 +26,11 @@ import android.database.Cursor; import android.net.Uri; import android.provider.BaseColumns; import android.provider.CalendarContract.Attendees; +import android.provider.CalendarContract.Calendars; +import android.provider.CalendarContract.Events; import android.provider.CalendarContract.Instances; import android.service.notification.ZenModeConfig.EventInfo; +import android.util.ArraySet; import android.util.Log; import java.io.PrintWriter; @@ -63,13 +66,15 @@ public class CalendarTracker { private static final String ATTENDEE_SELECTION = Attendees.EVENT_ID + " = ? AND " + Attendees.ATTENDEE_EMAIL + " = ?"; - private final Context mContext; + private final Context mSystemContext; + private final Context mUserContext; private Callback mCallback; private boolean mRegistered; - public CalendarTracker(Context context) { - mContext = context; + public CalendarTracker(Context systemContext, Context userContext) { + mSystemContext = systemContext; + mUserContext = userContext; } public void setCallback(Callback callback) { @@ -81,11 +86,12 @@ public class CalendarTracker { public void dump(String prefix, PrintWriter pw) { pw.print(prefix); pw.print("mCallback="); pw.println(mCallback); pw.print(prefix); pw.print("mRegistered="); pw.println(mRegistered); + pw.print(prefix); pw.print("u="); pw.println(mUserContext.getUserId()); } public void dumpContent(Uri uri) { Log.d(TAG, "dumpContent: " + uri); - final Cursor cursor = mContext.getContentResolver().query(uri, null, null, null, null); + final Cursor cursor = mUserContext.getContentResolver().query(uri, null, null, null, null); try { int r = 0; while (cursor.moveToNext()) { @@ -126,36 +132,61 @@ public class CalendarTracker { } } - + private ArraySet<Long> getPrimaryCalendars() { + final long start = System.currentTimeMillis(); + final ArraySet<Long> rt = new ArraySet<>(); + final String primary = "\"primary\""; + final String[] projection = { Calendars._ID, + "(" + Calendars.ACCOUNT_NAME + "=" + Calendars.OWNER_ACCOUNT + ") AS " + primary }; + final String selection = primary + " = 1"; + Cursor cursor = null; + try { + cursor = mUserContext.getContentResolver().query(Calendars.CONTENT_URI, projection, + selection, null, null); + while (cursor != null && cursor.moveToNext()) { + rt.add(cursor.getLong(0)); + } + } finally { + if (cursor != null) { + cursor.close(); + } + } + if (DEBUG) Log.d(TAG, "getPrimaryCalendars took " + (System.currentTimeMillis() - start)); + return rt; + } public CheckEventResult checkEvent(EventInfo filter, long time) { final Uri.Builder uriBuilder = Instances.CONTENT_URI.buildUpon(); ContentUris.appendId(uriBuilder, time); ContentUris.appendId(uriBuilder, time + EVENT_CHECK_LOOKAHEAD); final Uri uri = uriBuilder.build(); - final Cursor cursor = mContext.getContentResolver().query(uri, INSTANCE_PROJECTION, null, - null, INSTANCE_ORDER_BY); + final Cursor cursor = mUserContext.getContentResolver().query(uri, INSTANCE_PROJECTION, + null, null, INSTANCE_ORDER_BY); final CheckEventResult result = new CheckEventResult(); result.recheckAt = time + EVENT_CHECK_LOOKAHEAD; try { - while (cursor.moveToNext()) { + final ArraySet<Long> primaryCalendars = getPrimaryCalendars(); + while (cursor != null && cursor.moveToNext()) { final long begin = cursor.getLong(0); final long end = cursor.getLong(1); final String title = cursor.getString(2); - final boolean visible = cursor.getInt(3) == 1; + final boolean calendarVisible = cursor.getInt(3) == 1; final int eventId = cursor.getInt(4); final String owner = cursor.getString(5); final long calendarId = cursor.getLong(6); final int availability = cursor.getInt(7); - if (DEBUG) Log.d(TAG, String.format("%s %s-%s v=%s a=%s eid=%s o=%s cid=%s", title, - new Date(begin), new Date(end), visible, availabilityToString(availability), - eventId, owner, calendarId)); + final boolean calendarPrimary = primaryCalendars.contains(calendarId); + if (DEBUG) Log.d(TAG, String.format("%s %s-%s v=%s a=%s eid=%s o=%s cid=%s p=%s", + title, + new Date(begin), new Date(end), calendarVisible, + availabilityToString(availability), eventId, owner, calendarId, + calendarPrimary)); final boolean meetsTime = time >= begin && time < end; - final boolean meetsCalendar = visible - && (filter.calendar == ANY_CALENDAR || filter.calendar == calendarId) - && availability != Instances.AVAILABILITY_FREE; - if (meetsCalendar) { - if (DEBUG) Log.d(TAG, " MEETS CALENDAR"); + final boolean meetsCalendar = calendarVisible && calendarPrimary + && (filter.calendar == ANY_CALENDAR || filter.calendar == calendarId); + final boolean meetsAvailability = availability != Instances.AVAILABILITY_FREE; + if (meetsCalendar && meetsAvailability) { + if (DEBUG) Log.d(TAG, " MEETS CALENDAR & AVAILABILITY"); final boolean meetsAttendee = meetsAttendee(filter, eventId, owner); if (meetsAttendee) { if (DEBUG) Log.d(TAG, " MEETS ATTENDEE"); @@ -172,19 +203,22 @@ public class CalendarTracker { } } } finally { - cursor.close(); + if (cursor != null) { + cursor.close(); + } } return result; } private boolean meetsAttendee(EventInfo filter, int eventId, String email) { + final long start = System.currentTimeMillis(); String selection = ATTENDEE_SELECTION; String[] selectionArgs = { Integer.toString(eventId), email }; if (DEBUG_ATTENDEES) { selection = null; selectionArgs = null; } - final Cursor cursor = mContext.getContentResolver().query(Attendees.CONTENT_URI, + final Cursor cursor = mUserContext.getContentResolver().query(Attendees.CONTENT_URI, ATTENDEE_PROJECTION, selection, selectionArgs, null); try { if (cursor.getCount() == 0) { @@ -208,18 +242,25 @@ public class CalendarTracker { return rt; } finally { cursor.close(); + if (DEBUG) Log.d(TAG, "meetsAttendee took " + (System.currentTimeMillis() - start)); } } private void setRegistered(boolean registered) { if (mRegistered == registered) return; - final ContentResolver cr = mContext.getContentResolver(); + final ContentResolver cr = mSystemContext.getContentResolver(); + final int userId = mUserContext.getUserId(); if (mRegistered) { + if (DEBUG) Log.d(TAG, "unregister content observer u=" + userId); cr.unregisterContentObserver(mObserver); } mRegistered = registered; + if (DEBUG) Log.d(TAG, "mRegistered = " + registered + " u=" + userId); if (mRegistered) { - cr.registerContentObserver(Instances.CONTENT_URI, false, mObserver); + if (DEBUG) Log.d(TAG, "register content observer u=" + userId); + cr.registerContentObserver(Instances.CONTENT_URI, true, mObserver, userId); + cr.registerContentObserver(Events.CONTENT_URI, true, mObserver, userId); + cr.registerContentObserver(Calendars.CONTENT_URI, true, mObserver, userId); } } @@ -260,7 +301,8 @@ public class CalendarTracker { private final ContentObserver mObserver = new ContentObserver(null) { @Override public void onChange(boolean selfChange, Uri u) { - if (DEBUG) Log.d(TAG, "onChange selfChange=" + selfChange + " uri=" + u); + if (DEBUG) Log.d(TAG, "onChange selfChange=" + selfChange + " uri=" + u + + " u=" + mUserContext.getUserId()); mCallback.onChanged(); } diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java index 8c6afaf..33c666a 100644 --- a/services/core/java/com/android/server/notification/ConditionProviders.java +++ b/services/core/java/com/android/server/notification/ConditionProviders.java @@ -130,8 +130,8 @@ public class ConditionProviders extends ManagedServices { } @Override - public void onUserSwitched() { - super.onUserSwitched(); + public void onUserSwitched(int user) { + super.onUserSwitched(user); if (mCallback != null) { mCallback.onUserSwitched(); } diff --git a/services/core/java/com/android/server/notification/EventConditionProvider.java b/services/core/java/com/android/server/notification/EventConditionProvider.java index dea6325..46cc47b 100644 --- a/services/core/java/com/android/server/notification/EventConditionProvider.java +++ b/services/core/java/com/android/server/notification/EventConditionProvider.java @@ -23,7 +23,12 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager.NameNotFoundException; import android.net.Uri; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; +import android.os.UserManager; import android.service.notification.Condition; import android.service.notification.IConditionProvider; import android.service.notification.ZenModeConfig; @@ -31,6 +36,7 @@ import android.service.notification.ZenModeConfig.EventInfo; import android.util.ArraySet; import android.util.Log; import android.util.Slog; +import android.util.SparseArray; import com.android.server.notification.CalendarTracker.CheckEventResult; import com.android.server.notification.NotificationManagerService.DumpFilter; @@ -51,17 +57,21 @@ public class EventConditionProvider extends SystemConditionProviderService { private static final String ACTION_EVALUATE = SIMPLE_NAME + ".EVALUATE"; private static final int REQUEST_CODE_EVALUATE = 1; private static final String EXTRA_TIME = "time"; + private static final long CHANGE_DELAY = 2 * 1000; // coalesce chatty calendar changes private final Context mContext = this; private final ArraySet<Uri> mSubscriptions = new ArraySet<Uri>(); - private final CalendarTracker mTracker = new CalendarTracker(mContext); + private final SparseArray<CalendarTracker> mTrackers = new SparseArray<>(); + private final Handler mWorker; private boolean mConnected; private boolean mRegistered; private boolean mBootComplete; // don't hammer the calendar provider until boot completes. + private long mNextAlarmTime; - public EventConditionProvider() { + public EventConditionProvider(Looper worker) { if (DEBUG) Slog.d(TAG, "new " + SIMPLE_NAME + "()"); + mWorker = new Handler(worker); } @Override @@ -80,13 +90,17 @@ public class EventConditionProvider extends SystemConditionProviderService { pw.print(" mConnected="); pw.println(mConnected); pw.print(" mRegistered="); pw.println(mRegistered); pw.print(" mBootComplete="); pw.println(mBootComplete); + dumpUpcomingTime(pw, "mNextAlarmTime", mNextAlarmTime, System.currentTimeMillis()); pw.println(" mSubscriptions="); for (Uri conditionId : mSubscriptions) { pw.print(" "); pw.println(conditionId); } - pw.println(" mTracker="); - mTracker.dump(" ", pw); + pw.println(" mTrackers="); + for (int i = 0; i < mTrackers.size(); i++) { + pw.print(" user="); pw.println(mTrackers.keyAt(i)); + mTrackers.valueAt(i).dump(" ", pw); + } } @Override @@ -94,7 +108,16 @@ public class EventConditionProvider extends SystemConditionProviderService { if (DEBUG) Slog.d(TAG, "onBootComplete"); if (mBootComplete) return; mBootComplete = true; - evaluateSubscriptions(); + final IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED); + filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED); + mContext.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + reloadTrackers(); + } + }, filter); + reloadTrackers(); } @Override @@ -146,14 +169,39 @@ public class EventConditionProvider extends SystemConditionProviderService { return (IConditionProvider) onBind(null); } + private void reloadTrackers() { + if (DEBUG) Slog.d(TAG, "reloadTrackers"); + for (int i = 0; i < mTrackers.size(); i++) { + mTrackers.valueAt(i).setCallback(null); + } + mTrackers.clear(); + for (UserHandle user : UserManager.get(mContext).getUserProfiles()) { + final Context context = user.isOwner() ? mContext : getContextForUser(mContext, user); + if (context == null) { + Slog.w(TAG, "Unable to create context for user " + user.getIdentifier()); + continue; + } + mTrackers.put(user.getIdentifier(), new CalendarTracker(mContext, context)); + } + evaluateSubscriptions(); + } + private void evaluateSubscriptions() { - if (DEBUG) Log.d(TAG, "evaluateSubscriptions"); + if (!mWorker.hasCallbacks(mEvaluateSubscriptionsW)) { + mWorker.post(mEvaluateSubscriptionsW); + } + } + + private void evaluateSubscriptionsW() { + if (DEBUG) Slog.d(TAG, "evaluateSubscriptions"); if (!mBootComplete) { - if (DEBUG) Log.d(TAG, "Skipping evaluate before boot complete"); + if (DEBUG) Slog.d(TAG, "Skipping evaluate before boot complete"); return; } final long now = System.currentTimeMillis(); - mTracker.setCallback(mSubscriptions.isEmpty() ? null : mTrackerCallback); + for (int i = 0; i < mTrackers.size(); i++) { + mTrackers.valueAt(i).setCallback(mSubscriptions.isEmpty() ? null : mTrackerCallback); + } setRegistered(!mSubscriptions.isEmpty()); long reevaluateAt = 0; for (Uri conditionId : mSubscriptions) { @@ -162,7 +210,30 @@ public class EventConditionProvider extends SystemConditionProviderService { notifyCondition(conditionId, Condition.STATE_FALSE, "badConditionId"); continue; } - final CheckEventResult result = mTracker.checkEvent(event, now); + CheckEventResult result = null; + if (event.calendar == EventInfo.ANY_CALENDAR) { + // event could exist on any tracker + for (int i = 0; i < mTrackers.size(); i++) { + final CalendarTracker tracker = mTrackers.valueAt(i); + final CheckEventResult r = tracker.checkEvent(event, now); + if (result == null) { + result = r; + } else { + result.inEvent |= r.inEvent; + result.recheckAt = Math.min(result.recheckAt, r.recheckAt); + } + } + } else { + // event should exist on one tracker + final int userId = EventInfo.resolveUserId(event.userId); + final CalendarTracker tracker = mTrackers.get(userId); + if (tracker == null) { + Slog.w(TAG, "No calendar tracker found for user " + userId); + notifyCondition(conditionId, Condition.STATE_FALSE, "badUserId"); + continue; + } + result = tracker.checkEvent(event, now); + } if (result.recheckAt != 0 && (reevaluateAt == 0 || result.recheckAt < reevaluateAt)) { reevaluateAt = result.recheckAt; } @@ -172,11 +243,12 @@ public class EventConditionProvider extends SystemConditionProviderService { } notifyCondition(conditionId, Condition.STATE_TRUE, "inEventNow"); } - updateAlarm(now, reevaluateAt); - if (DEBUG) Log.d(TAG, "evaluateSubscriptions took " + (System.currentTimeMillis() - now)); + rescheduleAlarm(now, reevaluateAt); + if (DEBUG) Slog.d(TAG, "evaluateSubscriptions took " + (System.currentTimeMillis() - now)); } - private void updateAlarm(long now, long time) { + private void rescheduleAlarm(long now, long time) { + mNextAlarmTime = time; final AlarmManager alarms = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, REQUEST_CODE_EVALUATE, @@ -196,8 +268,8 @@ public class EventConditionProvider extends SystemConditionProviderService { } private void notifyCondition(Uri conditionId, int state, String reason) { - if (DEBUG) Slog.d(TAG, "notifyCondition " + Condition.stateToString(state) - + " reason=" + reason); + if (DEBUG) Slog.d(TAG, "notifyCondition " + conditionId + " " + + Condition.stateToString(state) + " reason=" + reason); notifyCondition(createCondition(conditionId, state)); } @@ -223,19 +295,35 @@ public class EventConditionProvider extends SystemConditionProviderService { } } + private static Context getContextForUser(Context context, UserHandle user) { + try { + return context.createPackageContextAsUser(context.getPackageName(), 0, user); + } catch (NameNotFoundException e) { + return null; + } + } + private final CalendarTracker.Callback mTrackerCallback = new CalendarTracker.Callback() { @Override public void onChanged() { - if (DEBUG) Log.d(TAG, "mTrackerCallback.onChanged"); - evaluateSubscriptions(); + if (DEBUG) Slog.d(TAG, "mTrackerCallback.onChanged"); + mWorker.removeCallbacks(mEvaluateSubscriptionsW); + mWorker.postDelayed(mEvaluateSubscriptionsW, CHANGE_DELAY); } }; - private BroadcastReceiver mReceiver = new BroadcastReceiver() { + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (DEBUG) Slog.d(TAG, "onReceive " + intent.getAction()); evaluateSubscriptions(); } }; + + private final Runnable mEvaluateSubscriptionsW = new Runnable() { + @Override + public void run() { + evaluateSubscriptionsW(); + } + }; } diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index 6f8e3ca..8f24b46 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -216,8 +216,8 @@ abstract public class ManagedServices { } } - public void onUserSwitched() { - if (DEBUG) Slog.d(TAG, "onUserSwitched"); + public void onUserSwitched(int user) { + if (DEBUG) Slog.d(TAG, "onUserSwitched u=" + user); if (Arrays.equals(mLastSeenProfileIds, mUserProfiles.getCurrentProfileIds())) { if (DEBUG) Slog.d(TAG, "Current profile IDs didn't change, skipping rebindServices()."); return; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index ef4da02..2be409a 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -799,12 +799,13 @@ public class NotificationManagerService extends SystemService { mNotificationLight.turnOff(); mStatusBar.notificationLightOff(); } else if (action.equals(Intent.ACTION_USER_SWITCHED)) { + final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); // reload per-user settings mSettingsObserver.update(null); mUserProfiles.updateCache(context); // Refresh managed services - mConditionProviders.onUserSwitched(); - mListeners.onUserSwitched(); + mConditionProviders.onUserSwitched(user); + mListeners.onUserSwitched(user); } else if (action.equals(Intent.ACTION_USER_ADDED)) { mUserProfiles.updateCache(context); } diff --git a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java index f06f54c..44448cc 100644 --- a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java +++ b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java @@ -85,12 +85,7 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { pw.print(meetsSchedule(conditionId, now) ? "* " : " "); pw.println(conditionId); } - pw.print(" mNextAlarmTime="); pw.print(mNextAlarmTime); - if (mNextAlarmTime > 0) { - pw.printf(" (%s, in %s, now=%s)", ts(mNextAlarmTime), - formatDuration(mNextAlarmTime - now), ts(now)); - } - pw.println(); + dumpUpcomingTime(pw, "mNextAlarmTime", mNextAlarmTime, now); } @Override diff --git a/services/core/java/com/android/server/notification/SystemConditionProviderService.java b/services/core/java/com/android/server/notification/SystemConditionProviderService.java index 8a8e063..574f04c 100644 --- a/services/core/java/com/android/server/notification/SystemConditionProviderService.java +++ b/services/core/java/com/android/server/notification/SystemConditionProviderService.java @@ -46,4 +46,14 @@ public abstract class SystemConditionProviderService extends ConditionProviderSe TimeUtils.formatDuration(millis, sb); return sb.toString(); } + + protected static void dumpUpcomingTime(PrintWriter pw, String var, long time, long now) { + pw.print(" "); pw.print(var); pw.print('='); + if (time > 0) { + pw.printf("%s, in %s, now=%s", ts(time), formatDuration(time - now), ts(now)); + } else { + pw.print(time); + } + pw.println(); + } } diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java index f1c58bd..d6b7f2f 100644 --- a/services/core/java/com/android/server/notification/ZenModeConditions.java +++ b/services/core/java/com/android/server/notification/ZenModeConditions.java @@ -50,7 +50,7 @@ public class ZenModeConditions implements ConditionProviders.Callback { mConditionProviders.addSystemProvider(new ScheduleConditionProvider()); } if (mConditionProviders.isSystemProviderEnabled(ZenModeConfig.EVENT_PATH)) { - mConditionProviders.addSystemProvider(new EventConditionProvider()); + mConditionProviders.addSystemProvider(new EventConditionProvider(helper.getLooper())); } mConditionProviders.setCallback(this); } diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index a3c36ed..1860673 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -98,6 +98,10 @@ public class ZenModeHelper { mConditions = new ZenModeConditions(this, conditionProviders); } + public Looper getLooper() { + return mHandler.getLooper(); + } + @Override public String toString() { return TAG; |