Merge "eliminate notification clicker garbage." into mnc-dev
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index ff589f9..49b2549 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2095,6 +2095,7 @@
 
                 try {
                     Constructor<? extends Style> constructor = styleClass.getConstructor();
+                    constructor.setAccessible(true);
                     style = constructor.newInstance();
                     style.restoreFromExtras(extras);
                 } catch (Throwable t) {
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 50e7d1c4..3fdabee 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -584,7 +584,7 @@
         // Nickname always takes precedence when defined
         if (!TextUtils.isEmpty(vol.fsUuid)) {
             final VolumeRecord rec = findRecordByUuid(vol.fsUuid);
-            if (!TextUtils.isEmpty(rec.nickname)) {
+            if (rec != null && !TextUtils.isEmpty(rec.nickname)) {
                 return rec.nickname;
             }
         }
diff --git a/core/java/android/preference/GenericInflater.java b/core/java/android/preference/GenericInflater.java
index 918933b..3319e64 100644
--- a/core/java/android/preference/GenericInflater.java
+++ b/core/java/android/preference/GenericInflater.java
@@ -376,6 +376,7 @@
                 Class clazz = mContext.getClassLoader().loadClass(
                         prefix != null ? (prefix + name) : name);
                 constructor = clazz.getConstructor(mConstructorSignature);
+                constructor.setAccessible(true);
                 sConstructorMap.put(name, constructor);
             }
 
diff --git a/core/java/android/transition/TransitionInflater.java b/core/java/android/transition/TransitionInflater.java
index ca37d49..cbf76bc 100644
--- a/core/java/android/transition/TransitionInflater.java
+++ b/core/java/android/transition/TransitionInflater.java
@@ -211,10 +211,10 @@
                             .asSubclass(expectedType);
                     if (c != null) {
                         constructor = c.getConstructor(sConstructorSignature);
+                        constructor.setAccessible(true);
                         sConstructors.put(className, constructor);
                     }
                 }
-                constructor.setAccessible(true);
                 return constructor.newInstance(mContext, attrs);
             }
         } catch (InstantiationException e) {
diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java
index cd68fd1..ed7fd86 100644
--- a/core/java/android/transition/Visibility.java
+++ b/core/java/android/transition/Visibility.java
@@ -19,12 +19,17 @@
 import com.android.internal.R;
 
 import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.Animator.AnimatorPauseListener;
 import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.view.animation.Animation.AnimationListener;
+
 /**
  * This transition tracks changes to the visibility of target views in the
  * start and end scenes. Visibility is determined not just by the
@@ -286,6 +291,12 @@
                 return null;
             }
         }
+        final boolean isForcedVisibility = mForcedStartVisibility != -1 ||
+                mForcedEndVisibility != -1;
+        if (isForcedVisibility) {
+            // Make sure that we reverse the effect of onDisappear's setTransitionAlpha(0)
+            endValues.view.setTransitionAlpha(1);
+        }
         return onAppear(sceneRoot, endValues.view, startValues, endValues);
     }
 
@@ -418,9 +429,9 @@
                 sceneRoot.getOverlay().remove(overlayView);
             } else {
                 final View finalOverlayView = overlayView;
-                animator.addListener(new AnimatorListenerAdapter() {
+                addListener(new TransitionListenerAdapter() {
                     @Override
-                    public void onAnimationEnd(Animator animation) {
+                    public void onTransitionEnd(Transition transition) {
                         finalSceneRoot.getOverlay().remove(finalOverlayView);
                     }
                 });
@@ -438,40 +449,10 @@
             }
             Animator animator = onDisappear(sceneRoot, viewToKeep, startValues, endValues);
             if (animator != null) {
-                final View finalViewToKeep = viewToKeep;
-                animator.addListener(new AnimatorListenerAdapter() {
-                    boolean mCanceled = false;
-
-                    @Override
-                    public void onAnimationPause(Animator animation) {
-                        if (!mCanceled && !isForcedVisibility) {
-                            finalViewToKeep.setVisibility(finalVisibility);
-                        }
-                    }
-
-                    @Override
-                    public void onAnimationResume(Animator animation) {
-                        if (!mCanceled && !isForcedVisibility) {
-                            finalViewToKeep.setVisibility(View.VISIBLE);
-                        }
-                    }
-
-                    @Override
-                    public void onAnimationCancel(Animator animation) {
-                        mCanceled = true;
-                    }
-
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        if (!mCanceled) {
-                            if (isForcedVisibility) {
-                                finalViewToKeep.setTransitionAlpha(0);
-                            } else {
-                                finalViewToKeep.setVisibility(finalVisibility);
-                            }
-                        }
-                    }
-                });
+                DisappearListener disappearListener = new DisappearListener(viewToKeep,
+                        finalVisibility, isForcedVisibility);
+                animator.addListener(disappearListener);
+                addListener(disappearListener);
             } else if (!isForcedVisibility) {
                 viewToKeep.setVisibility(originalVisibility);
             }
@@ -517,4 +498,68 @@
             TransitionValues endValues) {
         return null;
     }
+
+    private static class DisappearListener
+            extends TransitionListenerAdapter implements AnimatorListener, AnimatorPauseListener {
+        private final boolean mIsForcedVisibility;
+        private final View mView;
+        private final int mFinalVisibility;
+
+        boolean mCanceled = false;
+
+        public DisappearListener(View view, int finalVisibility, boolean isForcedVisibility) {
+            this.mView = view;
+            this.mIsForcedVisibility = isForcedVisibility;
+            this.mFinalVisibility = finalVisibility;
+        }
+
+        @Override
+        public void onAnimationPause(Animator animation) {
+            if (!mCanceled && !mIsForcedVisibility) {
+                mView.setVisibility(mFinalVisibility);
+            }
+        }
+
+        @Override
+        public void onAnimationResume(Animator animation) {
+            if (!mCanceled && !mIsForcedVisibility) {
+                mView.setVisibility(View.VISIBLE);
+            }
+        }
+
+        @Override
+        public void onAnimationCancel(Animator animation) {
+            mCanceled = true;
+        }
+
+        @Override
+        public void onAnimationRepeat(Animator animation) {
+
+        }
+
+        @Override
+        public void onAnimationStart(Animator animation) {
+
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            hideViewWhenNotCanceled();
+        }
+
+        @Override
+        public void onTransitionEnd(Transition transition) {
+            hideViewWhenNotCanceled();
+        }
+
+        private void hideViewWhenNotCanceled() {
+            if (!mCanceled) {
+                if (mIsForcedVisibility) {
+                    mView.setTransitionAlpha(0);
+                } else {
+                    mView.setVisibility(mFinalVisibility);
+                }
+            }
+        }
+    }
 }
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index 1503728..37312d0 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -590,6 +590,7 @@
                     }
                 }
                 constructor = clazz.getConstructor(mConstructorSignature);
+                constructor.setAccessible(true);
                 sConstructorMap.put(name, constructor);
             } else {
                 // If we have a filter, apply it to cached constructor
@@ -615,7 +616,6 @@
             Object[] args = mConstructorArgs;
             args[1] = attrs;
 
-            constructor.setAccessible(true);
             final View view = constructor.newInstance(args);
             if (view instanceof ViewStub) {
                 // Use the same context when inflating ViewStub later.
diff --git a/core/java/android/view/MenuInflater.java b/core/java/android/view/MenuInflater.java
index b49a59e..dc8cadf 100644
--- a/core/java/android/view/MenuInflater.java
+++ b/core/java/android/view/MenuInflater.java
@@ -544,6 +544,7 @@
             try {
                 Class<?> clazz = mContext.getClassLoader().loadClass(className);
                 Constructor<?> constructor = clazz.getConstructor(constructorSignature);
+                constructor.setAccessible(true);
                 return (T) constructor.newInstance(arguments);
             } catch (Exception e) {
                 Log.w(LOG_TAG, "Cannot instantiate class: " + className, e);
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index f67dcb3..efc171c 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -53,8 +53,11 @@
  * attribute identifier.
  * <p>
  * A touch feedback drawable may contain multiple child layers, including a
- * special mask layer that is not drawn to the screen. A single layer may be set
- * as the mask by specifying its android:id value as {@link android.R.id#mask}.
+ * special mask layer that is not drawn to the screen. A single layer may be
+ * set as the mask from XML by specifying its {@code android:id} value as
+ * {@link android.R.id#mask}. At run time, a single layer may be set as the
+ * mask using {@code setId(..., android.R.id.mask)} or an existing mask layer
+ * may be replaced using {@code setDrawableByLayerId(android.R.id.mask, ...)}.
  * <pre>
  * <code>&lt!-- A red ripple masked against an opaque rectangle. --/>
  * &ltripple android:color="#ffff0000">
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index d15fa39..7c04f40 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -135,7 +135,7 @@
     SETUP_TASK(setName);
     args->context = mContext;
     args->name = name;
-    post(task);
+    postAndWait(task);
 }
 
 CREATE_BRIDGE2(initialize, CanvasContext* context, ANativeWindow* window) {
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 6876222..b2c9cd0 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -72,6 +72,9 @@
     <!-- Status message of Wi-Fi when network has matching passpoint credentials. [CHAR LIMIT=NONE] -->
     <string name="available_via_passpoint">Available via %1$s</string>
 
+    <!-- Summary for Connected wifi network without internet -->
+    <string name="wifi_connected_no_internet">Connected, no Internet</string>
+
     <!-- Bluetooth settings.  Message when a device is disconnected -->
     <string name="bluetooth_disconnected">Disconnected</string>
     <!-- Bluetooth settings.  Message when disconnecting from a device -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 2fde4f9..53e69e3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -17,9 +17,13 @@
 package com.android.settingslib.wifi;
 
 import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkInfo.State;
+import android.net.wifi.IWifiManager;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
@@ -27,6 +31,8 @@
 import android.net.wifi.WifiManager;
 import android.os.Bundle;
 import android.text.TextUtils;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.util.Log;
 import android.util.LruCache;
 
@@ -700,6 +706,25 @@
             }
         }
 
+        // Case when there is wifi connected without internet connectivity.
+        final ConnectivityManager cm = (ConnectivityManager)
+                context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        if (state == DetailedState.CONNECTED) {
+            IWifiManager wifiManager = IWifiManager.Stub.asInterface(
+                    ServiceManager.getService(Context.WIFI_SERVICE));
+            Network nw;
+
+            try {
+                nw = wifiManager.getCurrentNetwork();
+            } catch (RemoteException e) {
+                nw = null;
+            }
+            NetworkCapabilities nc = cm.getNetworkCapabilities(nw);
+            if (nc != null && !nc.hasCapability(nc.NET_CAPABILITY_VALIDATED)) {
+                return context.getString(R.string.wifi_connected_no_internet);
+            }
+        }
+
         String[] formats = context.getResources().getStringArray((ssid == null)
                 ? R.array.wifi_status : R.array.wifi_status_with_ssid);
         int index = state.ordinal();
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 1a75b8a..8d1d124 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1141,7 +1141,13 @@
         NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
         if (nai != null) {
             synchronized (nai) {
-                return new NetworkCapabilities(nai.networkCapabilities);
+                NetworkCapabilities nc = new NetworkCapabilities(nai.networkCapabilities);
+                if (nai.lastValidated) {
+                    nc.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+                } else {
+                    nc.removeCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+                }
+                return nc;
             }
         }
         return null;
diff --git a/services/core/java/com/android/server/notification/CalendarTracker.java b/services/core/java/com/android/server/notification/CalendarTracker.java
new file mode 100644
index 0000000..8734d97
--- /dev/null
+++ b/services/core/java/com/android/server/notification/CalendarTracker.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.BaseColumns;
+import android.provider.CalendarContract.Attendees;
+import android.provider.CalendarContract.Instances;
+import android.service.notification.ZenModeConfig.EventInfo;
+import android.util.Log;
+
+import java.io.PrintWriter;
+import java.util.Date;
+import java.util.Objects;
+
+public class CalendarTracker {
+    private static final String TAG = "ConditionProviders.CT";
+    private static final boolean DEBUG = Log.isLoggable("ConditionProviders", Log.DEBUG);
+    private static final boolean DEBUG_ATTENDEES = false;
+
+    private static final int EVENT_CHECK_LOOKAHEAD = 24 * 60 * 60 * 1000;
+
+    private static final String[] INSTANCE_PROJECTION = {
+        Instances.BEGIN,
+        Instances.END,
+        Instances.TITLE,
+        Instances.VISIBLE,
+        Instances.EVENT_ID,
+        Instances.OWNER_ACCOUNT,
+        Instances.CALENDAR_ID,
+    };
+
+    private static final String INSTANCE_ORDER_BY = Instances.BEGIN + " ASC";
+
+    private static final String[] ATTENDEE_PROJECTION = {
+        Attendees.EVENT_ID,
+        Attendees.ATTENDEE_EMAIL,
+        Attendees.ATTENDEE_STATUS,
+        Attendees.ATTENDEE_TYPE,
+    };
+
+    private static final String ATTENDEE_SELECTION = Attendees.EVENT_ID + " = ? AND "
+            + Attendees.ATTENDEE_EMAIL + " = ?";
+
+    private final Context mContext;
+
+    private Callback mCallback;
+    private boolean mRegistered;
+
+    public CalendarTracker(Context context) {
+        mContext = context;
+    }
+
+    public void setCallback(Callback callback) {
+        if (mCallback == callback) return;
+        mCallback = callback;
+        setRegistered(mCallback != null);
+    }
+
+    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);
+    }
+
+    public void dumpContent(Uri uri) {
+        Log.d(TAG, "dumpContent: " + uri);
+        final Cursor cursor = mContext.getContentResolver().query(uri, null, null, null, null);
+        try {
+            int r = 0;
+            while (cursor.moveToNext()) {
+                Log.d(TAG, "Row " + (++r) + ": id="
+                        + cursor.getInt(cursor.getColumnIndex(BaseColumns._ID)));
+                for (int i = 0; i < cursor.getColumnCount(); i++) {
+                    final String name = cursor.getColumnName(i);
+                    final int type = cursor.getType(i);
+                    Object o = null;
+                    String typeName = null;
+                    switch (type) {
+                        case Cursor.FIELD_TYPE_INTEGER:
+                            o = cursor.getLong(i);
+                            typeName = "INTEGER";
+                            break;
+                        case Cursor.FIELD_TYPE_STRING:
+                            o = cursor.getString(i);
+                            typeName = "STRING";
+                            break;
+                        case Cursor.FIELD_TYPE_NULL:
+                            o = null;
+                            typeName = "NULL";
+                            break;
+                        default:
+                            throw new UnsupportedOperationException("type: " + type);
+                    }
+                    if (name.equals(BaseColumns._ID)
+                            || name.toLowerCase().contains("sync")
+                            || o == null) {
+                        continue;
+                    }
+                    Log.d(TAG, "  " + name + "(" + typeName + ")=" + o);
+                }
+            }
+            Log.d(TAG, "  " + uri + " " + r + " rows");
+        } finally {
+            cursor.close();
+        }
+    }
+
+
+
+    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 CheckEventResult result = new CheckEventResult();
+        result.recheckAt = time + EVENT_CHECK_LOOKAHEAD;
+        try {
+            while (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 int eventId = cursor.getInt(4);
+                final String owner = cursor.getString(5);
+                final long calendarId = cursor.getLong(6);
+                if (DEBUG) Log.d(TAG, String.format("%s %s-%s v=%s eid=%s o=%s cid=%s", title,
+                        new Date(begin), new Date(end), visible, eventId, owner, calendarId));
+                final boolean meetsTime = time >= begin && time < end;
+                final boolean meetsCalendar = visible
+                        && (filter.calendar == 0 || filter.calendar == calendarId);
+                if (meetsCalendar) {
+                    if (DEBUG) Log.d(TAG, "  MEETS CALENDAR");
+                    final boolean meetsAttendee = meetsAttendee(filter, eventId, owner);
+                    if (meetsAttendee) {
+                        if (DEBUG) Log.d(TAG, "    MEETS ATTENDEE");
+                        if (meetsTime) {
+                            if (DEBUG) Log.d(TAG, "      MEETS TIME");
+                            result.inEvent = true;
+                        }
+                        if (begin > time && begin < result.recheckAt) {
+                            result.recheckAt = begin;
+                        } else if (end > time && end < result.recheckAt) {
+                            result.recheckAt = end;
+                        }
+                    }
+                }
+            }
+        } finally {
+            cursor.close();
+        }
+        return result;
+    }
+
+    private boolean meetsAttendee(EventInfo filter, int eventId, String email) {
+        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,
+                ATTENDEE_PROJECTION, selection, selectionArgs, null);
+        try {
+            if (cursor.getCount() == 0) {
+                if (DEBUG) Log.d(TAG, "No attendees found");
+                return true;
+            }
+            boolean rt = false;
+            while (cursor.moveToNext()) {
+                final long rowEventId = cursor.getLong(0);
+                final String rowEmail = cursor.getString(1);
+                final int status = cursor.getInt(2);
+                final int type = cursor.getInt(3);
+                final boolean meetsReply = meetsReply(filter.reply, status);
+                final boolean meetsAttendance = meetsAttendance(filter.attendance, type);
+                if (DEBUG) Log.d(TAG, (DEBUG_ATTENDEES ? String.format(
+                        "rowEventId=%s, rowEmail=%s, ", rowEventId, rowEmail) : "") +
+                        String.format("status=%s, type=%s, meetsReply=%s, meetsAttendance=%s",
+                        attendeeStatusToString(status), attendeeTypeToString(type), meetsReply,
+                        meetsAttendance));
+                final boolean eventMeets = rowEventId == eventId && Objects.equals(rowEmail, email)
+                        && meetsReply && meetsAttendance;
+                rt |= eventMeets;
+            }
+            return rt;
+        } finally {
+            cursor.close();
+        }
+    }
+
+    private void setRegistered(boolean registered) {
+        if (mRegistered == registered) return;
+        final ContentResolver cr = mContext.getContentResolver();
+        if (mRegistered) {
+            cr.unregisterContentObserver(mObserver);
+        }
+        mRegistered = registered;
+        if (mRegistered) {
+            cr.registerContentObserver(Instances.CONTENT_URI, false, mObserver);
+        }
+    }
+
+    private static String attendeeStatusToString(int status) {
+        switch (status) {
+            case Attendees.ATTENDEE_STATUS_NONE: return "ATTENDEE_STATUS_NONE";
+            case Attendees.ATTENDEE_STATUS_ACCEPTED: return "ATTENDEE_STATUS_ACCEPTED";
+            case Attendees.ATTENDEE_STATUS_DECLINED: return "ATTENDEE_STATUS_DECLINED";
+            case Attendees.ATTENDEE_STATUS_INVITED: return "ATTENDEE_STATUS_INVITED";
+            case Attendees.ATTENDEE_STATUS_TENTATIVE: return "ATTENDEE_STATUS_TENTATIVE";
+            default: return "ATTENDEE_STATUS_UNKNOWN_" + status;
+        }
+    }
+
+    private static String attendeeTypeToString(int type) {
+        switch (type) {
+            case Attendees.TYPE_NONE: return "TYPE_NONE";
+            case Attendees.TYPE_REQUIRED: return "TYPE_REQUIRED";
+            case Attendees.TYPE_OPTIONAL: return "TYPE_OPTIONAL";
+            case Attendees.TYPE_RESOURCE: return "TYPE_RESOURCE";
+            default: return "TYPE_" + type;
+        }
+    }
+
+    private static boolean meetsAttendance(int attendance, int attendeeType) {
+        switch (attendance) {
+            case EventInfo.ATTENDANCE_OPTIONAL:
+                return attendeeType == Attendees.TYPE_OPTIONAL;
+            case EventInfo.ATTENDANCE_REQUIRED:
+                return attendeeType == Attendees.TYPE_REQUIRED;
+            default: // EventInfo.ATTENDANCE_REQUIRED_OR_OPTIONAL
+                return true;
+        }
+    }
+
+    private static boolean meetsReply(int reply, int attendeeStatus) {
+        switch (reply) {
+            case EventInfo.REPLY_YES:
+                return attendeeStatus == Attendees.ATTENDEE_STATUS_ACCEPTED;
+            case EventInfo.REPLY_ANY_EXCEPT_NO:
+                return attendeeStatus != Attendees.ATTENDEE_STATUS_DECLINED;
+            default: // EventInfo.REPLY_ANY
+                return true;
+        }
+    }
+
+    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);
+            mCallback.onChanged();
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            if (DEBUG) Log.d(TAG, "onChange selfChange=" + selfChange);
+        }
+    };
+
+    public static class CheckEventResult {
+        public boolean inEvent;
+        public long recheckAt;
+    }
+
+    public interface Callback {
+        void onChanged();
+    }
+
+}
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index b36fcd2..8c6afaf 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -121,6 +121,9 @@
     @Override
     public void onBootPhaseAppsCanStart() {
         super.onBootPhaseAppsCanStart();
+        for (int i = 0; i < mSystemConditionProviders.size(); i++) {
+            mSystemConditionProviders.valueAt(i).onBootComplete();
+        }
         if (mCallback != null) {
             mCallback.onBootComplete();
         }
diff --git a/services/core/java/com/android/server/notification/CountdownConditionProvider.java b/services/core/java/com/android/server/notification/CountdownConditionProvider.java
index b5b97d6..07903e9 100644
--- a/services/core/java/com/android/server/notification/CountdownConditionProvider.java
+++ b/services/core/java/com/android/server/notification/CountdownConditionProvider.java
@@ -34,12 +34,11 @@
 import com.android.server.notification.NotificationManagerService.DumpFilter;
 
 import java.io.PrintWriter;
-import java.util.Date;
 
 /** Built-in zen condition provider for simple time-based conditions */
 public class CountdownConditionProvider extends SystemConditionProviderService {
-    private static final String TAG = "ConditionProviders";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final String TAG = "ConditionProviders.CCP";
+    private static final boolean DEBUG = Log.isLoggable("ConditionProviders", Log.DEBUG);
 
     public static final ComponentName COMPONENT =
             new ComponentName("android", CountdownConditionProvider.class.getName());
@@ -74,6 +73,11 @@
     }
 
     @Override
+    public void onBootComplete() {
+        // noop
+    }
+
+    @Override
     public IConditionProvider asInterface() {
         return (IConditionProvider) onBind(null);
     }
@@ -170,8 +174,4 @@
                 ts(time), time - now, span, ts(now));
     }
 
-    private static String ts(long time) {
-        return new Date(time) + " (" + time + ")";
-    }
-
 }
diff --git a/services/core/java/com/android/server/notification/EventConditionProvider.java b/services/core/java/com/android/server/notification/EventConditionProvider.java
index 425e222..dea6325 100644
--- a/services/core/java/com/android/server/notification/EventConditionProvider.java
+++ b/services/core/java/com/android/server/notification/EventConditionProvider.java
@@ -16,16 +16,23 @@
 
 package com.android.server.notification;
 
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.net.Uri;
 import android.service.notification.Condition;
 import android.service.notification.IConditionProvider;
 import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenModeConfig.EventInfo;
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Slog;
 
+import com.android.server.notification.CalendarTracker.CheckEventResult;
 import com.android.server.notification.NotificationManagerService.DumpFilter;
 
 import java.io.PrintWriter;
@@ -34,20 +41,27 @@
  * Built-in zen condition provider for calendar event-based conditions.
  */
 public class EventConditionProvider extends SystemConditionProviderService {
-    private static final String TAG = "ConditionProviders";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final String TAG = "ConditionProviders.ECP";
+    private static final boolean DEBUG = Log.isLoggable("ConditionProviders", Log.DEBUG);
 
     public static final ComponentName COMPONENT =
             new ComponentName("android", EventConditionProvider.class.getName());
     private static final String NOT_SHOWN = "...";
+    private static final String SIMPLE_NAME = EventConditionProvider.class.getSimpleName();
+    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 final Context mContext = this;
     private final ArraySet<Uri> mSubscriptions = new ArraySet<Uri>();
+    private final CalendarTracker mTracker = new CalendarTracker(mContext);
 
     private boolean mConnected;
     private boolean mRegistered;
+    private boolean mBootComplete;  // don't hammer the calendar provider until boot completes.
 
     public EventConditionProvider() {
-        if (DEBUG) Slog.d(TAG, "new EventConditionProvider()");
+        if (DEBUG) Slog.d(TAG, "new " + SIMPLE_NAME + "()");
     }
 
     @Override
@@ -62,14 +76,25 @@
 
     @Override
     public void dump(PrintWriter pw, DumpFilter filter) {
-        pw.println("    EventConditionProvider:");
+        pw.print("    "); pw.print(SIMPLE_NAME); pw.println(":");
         pw.print("      mConnected="); pw.println(mConnected);
         pw.print("      mRegistered="); pw.println(mRegistered);
+        pw.print("      mBootComplete="); pw.println(mBootComplete);
         pw.println("      mSubscriptions=");
         for (Uri conditionId : mSubscriptions) {
             pw.print("        ");
             pw.println(conditionId);
         }
+        pw.println("      mTracker=");
+        mTracker.dump("        ", pw);
+    }
+
+    @Override
+    public void onBootComplete() {
+        if (DEBUG) Slog.d(TAG, "onBootComplete");
+        if (mBootComplete) return;
+        mBootComplete = true;
+        evaluateSubscriptions();
     }
 
     @Override
@@ -98,8 +123,9 @@
             notifyCondition(conditionId, Condition.STATE_FALSE, "badCondition");
             return;
         }
-        mSubscriptions.add(conditionId);
-        evaluateSubscriptions();
+        if (mSubscriptions.add(conditionId)) {
+            evaluateSubscriptions();
+        }
     }
 
     @Override
@@ -121,9 +147,52 @@
     }
 
     private void evaluateSubscriptions() {
-        for (Uri conditionId : mSubscriptions) {
-            notifyCondition(conditionId, Condition.STATE_FALSE, "notImplemented");
+        if (DEBUG) Log.d(TAG, "evaluateSubscriptions");
+        if (!mBootComplete) {
+            if (DEBUG) Log.d(TAG, "Skipping evaluate before boot complete");
+            return;
         }
+        final long now = System.currentTimeMillis();
+        mTracker.setCallback(mSubscriptions.isEmpty() ? null : mTrackerCallback);
+        setRegistered(!mSubscriptions.isEmpty());
+        long reevaluateAt = 0;
+        for (Uri conditionId : mSubscriptions) {
+            final EventInfo event = ZenModeConfig.tryParseEventConditionId(conditionId);
+            if (event == null) {
+                notifyCondition(conditionId, Condition.STATE_FALSE, "badConditionId");
+                continue;
+            }
+            final CheckEventResult result = mTracker.checkEvent(event, now);
+            if (result.recheckAt != 0 && (reevaluateAt == 0 || result.recheckAt < reevaluateAt)) {
+                reevaluateAt = result.recheckAt;
+            }
+            if (!result.inEvent) {
+                notifyCondition(conditionId, Condition.STATE_FALSE, "!inEventNow");
+                continue;
+            }
+            notifyCondition(conditionId, Condition.STATE_TRUE, "inEventNow");
+        }
+        updateAlarm(now, reevaluateAt);
+        if (DEBUG) Log.d(TAG, "evaluateSubscriptions took " + (System.currentTimeMillis() - now));
+    }
+
+    private void updateAlarm(long now, long time) {
+        final AlarmManager alarms = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+        final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext,
+                REQUEST_CODE_EVALUATE,
+                new Intent(ACTION_EVALUATE)
+                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+                        .putExtra(EXTRA_TIME, time),
+                PendingIntent.FLAG_UPDATE_CURRENT);
+        alarms.cancel(pendingIntent);
+        if (time == 0 || time < now) {
+            if (DEBUG) Slog.d(TAG, "Not scheduling evaluate: " + (time == 0 ? "no time specified"
+                    : "specified time in the past"));
+            return;
+        }
+        if (DEBUG) Slog.d(TAG, String.format("Scheduling evaluate for %s, in %s, now=%s",
+                ts(time), formatDuration(time - now), ts(now)));
+        alarms.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent);
     }
 
     private void notifyCondition(Uri conditionId, int state, String reason) {
@@ -139,4 +208,34 @@
         return new Condition(id, summary, line1, line2, 0, state, Condition.FLAG_RELEVANT_ALWAYS);
     }
 
+    private void setRegistered(boolean registered) {
+        if (mRegistered == registered) return;
+        if (DEBUG) Slog.d(TAG, "setRegistered " + registered);
+        mRegistered = registered;
+        if (mRegistered) {
+            final IntentFilter filter = new IntentFilter();
+            filter.addAction(Intent.ACTION_TIME_CHANGED);
+            filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+            filter.addAction(ACTION_EVALUATE);
+            registerReceiver(mReceiver, filter);
+        } else {
+            unregisterReceiver(mReceiver);
+        }
+    }
+
+    private final CalendarTracker.Callback mTrackerCallback = new CalendarTracker.Callback() {
+        @Override
+        public void onChanged() {
+            if (DEBUG) Log.d(TAG, "mTrackerCallback.onChanged");
+            evaluateSubscriptions();
+        }
+    };
+
+    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (DEBUG) Slog.d(TAG, "onReceive " + intent.getAction());
+            evaluateSubscriptions();
+        }
+    };
 }
diff --git a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
index 0912e97..bca13c2 100644
--- a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
+++ b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
@@ -31,25 +31,24 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Slog;
-import android.util.TimeUtils;
 
 import com.android.server.notification.NotificationManagerService.DumpFilter;
 
 import java.io.PrintWriter;
-import java.util.Date;
 import java.util.TimeZone;
 
 /**
  * Built-in zen condition provider for daily scheduled time-based conditions.
  */
 public class ScheduleConditionProvider extends SystemConditionProviderService {
-    private static final String TAG = "ConditionProviders";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final String TAG = "ConditionProviders.SCP";
+    private static final boolean DEBUG = Log.isLoggable("ConditionProviders", Log.DEBUG);
 
     public static final ComponentName COMPONENT =
             new ComponentName("android", ScheduleConditionProvider.class.getName());
     private static final String NOT_SHOWN = "...";
-    private static final String ACTION_EVALUATE = TAG + ".EVALUATE";
+    private static final String SIMPLE_NAME = ScheduleConditionProvider.class.getSimpleName();
+    private static final String ACTION_EVALUATE =  SIMPLE_NAME + ".EVALUATE";
     private static final int REQUEST_CODE_EVALUATE = 1;
     private static final String EXTRA_TIME = "time";
 
@@ -60,7 +59,7 @@
     private boolean mRegistered;
 
     public ScheduleConditionProvider() {
-        if (DEBUG) Slog.d(TAG, "new ScheduleConditionProvider()");
+        if (DEBUG) Slog.d(TAG, "new " + SIMPLE_NAME + "()");
     }
 
     @Override
@@ -75,7 +74,7 @@
 
     @Override
     public void dump(PrintWriter pw, DumpFilter filter) {
-        pw.println("    ScheduleConditionProvider:");
+        pw.print("    "); pw.print(SIMPLE_NAME); pw.println(":");
         pw.print("      mConnected="); pw.println(mConnected);
         pw.print("      mRegistered="); pw.println(mRegistered);
         pw.println("      mSubscriptions=");
@@ -94,6 +93,11 @@
     }
 
     @Override
+    public void onBootComplete() {
+        // noop
+    }
+
+    @Override
     public void onDestroy() {
         super.onDestroy();
         if (DEBUG) Slog.d(TAG, "onDestroy");
@@ -175,16 +179,6 @@
         }
     }
 
-    private static String ts(long time) {
-        return new Date(time) + " (" + time + ")";
-    }
-
-    private static String formatDuration(long millis) {
-        final StringBuilder sb = new StringBuilder();
-        TimeUtils.formatDuration(millis, sb);
-        return sb.toString();
-    }
-
     private static boolean meetsSchedule(Uri conditionId, long time) {
         final ScheduleCalendar cal = toScheduleCalendar(conditionId);
         return cal != null && cal.isInSchedule(time);
diff --git a/services/core/java/com/android/server/notification/SystemConditionProviderService.java b/services/core/java/com/android/server/notification/SystemConditionProviderService.java
index 3282a69a..8a8e063 100644
--- a/services/core/java/com/android/server/notification/SystemConditionProviderService.java
+++ b/services/core/java/com/android/server/notification/SystemConditionProviderService.java
@@ -21,10 +21,12 @@
 import android.net.Uri;
 import android.service.notification.ConditionProviderService;
 import android.service.notification.IConditionProvider;
+import android.util.TimeUtils;
 
 import com.android.server.notification.NotificationManagerService.DumpFilter;
 
 import java.io.PrintWriter;
+import java.util.Date;
 
 public abstract class SystemConditionProviderService extends ConditionProviderService {
 
@@ -33,4 +35,15 @@
     abstract public IConditionProvider asInterface();
     abstract public ComponentName getComponent();
     abstract public boolean isValidConditionId(Uri id);
+    abstract public void onBootComplete();
+
+    protected static String ts(long time) {
+        return new Date(time) + " (" + time + ")";
+    }
+
+    protected static String formatDuration(long millis) {
+        final StringBuilder sb = new StringBuilder();
+        TimeUtils.formatDuration(millis, sb);
+        return sb.toString();
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 66dd8f1..0feded3 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -9149,7 +9149,11 @@
     @Override
     public boolean setDefaultBrowserPackageName(String packageName, int userId) {
         synchronized (mPackages) {
-            return mSettings.setDefaultBrowserPackageNameLPr(packageName, userId);
+            boolean result = mSettings.setDefaultBrowserPackageNameLPr(packageName, userId);
+            result |= updateIntentVerificationStatus(packageName,
+                    PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS,
+                    UserHandle.myUserId());
+            return result;
         }
     }
 
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 0e888e8..00cdc71 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -25,6 +25,7 @@
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConnectionStatistics;
 import android.net.wifi.WifiActivityEnergyInfo;
+import android.net.Network;
 
 import android.net.DhcpInfo;
 
@@ -168,5 +169,7 @@
     void disableEphemeralNetwork(String SSID);
 
     void factoryReset();
+
+    Network getCurrentNetwork();
 }
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 3205351..5e996725 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2804,4 +2804,17 @@
         } catch (RemoteException e) {
         }
     }
+
+    /**
+     * Get Network object of current wifi network
+     * @return Get Network object of current wifi network
+     * @hide
+     */
+    public Network getCurrentNetwork() {
+        try {
+            return mService.getCurrentNetwork();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
 }