Wire up condition providers to zen mode exit triggers.

Bug:13743109
Change-Id: I4e45d7050d1f9aaa379f46379a3203e61e216a3d
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index a282270..2c5d69c 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -17,21 +17,40 @@
 package com.android.server.notification;
 
 import android.content.Context;
+import android.net.Uri;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IInterface;
+import android.os.RemoteException;
 import android.provider.Settings;
-import android.service.notification.IConditionProvider;
+import android.service.notification.Condition;
 import android.service.notification.ConditionProviderService;
+import android.service.notification.IConditionListener;
+import android.service.notification.IConditionProvider;
+import android.util.ArrayMap;
 import android.util.Slog;
 
 import com.android.internal.R;
 
+import libcore.util.Objects;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+
 public class ConditionProviders extends ManagedServices {
 
+    private final ZenModeHelper mZenModeHelper;
+    private final ArrayMap<IBinder, IConditionListener> mListeners
+            = new ArrayMap<IBinder, IConditionListener>();
+    private final ArrayMap<Uri, ManagedServiceInfo> mConditions
+            = new ArrayMap<Uri, ManagedServiceInfo>();
+
+    private Uri mCurrentConditionId;
+
     public ConditionProviders(Context context, Handler handler,
-            Object mutex, UserProfiles userProfiles) {
-        super(context, handler, mutex, userProfiles);
+            UserProfiles userProfiles, ZenModeHelper zenModeHelper) {
+        super(context, handler, new Object(), userProfiles);
+        mZenModeHelper = zenModeHelper;
     }
 
     @Override
@@ -47,6 +66,28 @@
     }
 
     @Override
+    public void dump(PrintWriter pw) {
+        super.dump(pw);
+        synchronized(mMutex) {
+            pw.print("    mCurrentConditionId="); pw.println(mCurrentConditionId);
+            pw.print("    mListeners("); pw.print(mListeners.size()); pw.println("):");
+            for (int i = 0; i < mListeners.size(); i++) {
+                pw.print("      "); pw.println(mListeners.keyAt(i));
+            }
+            pw.print("    mConditions("); pw.print(mConditions.size()); pw.println("):");
+            for (int i = 0; i < mConditions.size(); i++) {
+                pw.print("      "); pw.print(mConditions.keyAt(i));
+                final ManagedServiceInfo info = mConditions.valueAt(i);
+                pw.print(" -> "); pw.print(info.component);
+                if (!mServices.contains(info)) {
+                    pw.print(" (orphan)");
+                }
+                pw.println();
+            }
+        }
+    }
+
+    @Override
     protected IInterface asInterface(IBinder binder) {
         return IConditionProvider.Stub.asInterface(binder);
     }
@@ -54,5 +95,144 @@
     @Override
     protected void onServiceAdded(IInterface service) {
         Slog.d(TAG, "onServiceAdded " + service);
+        final IConditionProvider provider = (IConditionProvider) service;
+        try {
+            provider.onConnected();
+        } catch (RemoteException e) {
+            // we tried
+        }
+    }
+
+    @Override
+    protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
+        if (removed == null) return;
+        for (int i = mConditions.size() - 1; i >= 0; i--) {
+            if (removed.equals(mConditions.valueAt(i))) {
+                mConditions.removeAt(i);
+            }
+        }
+    }
+
+    public ManagedServiceInfo checkServiceToken(IConditionProvider provider) {
+        synchronized(mMutex) {
+            return checkServiceTokenLocked(provider);
+        }
+    }
+
+    public void requestZenModeConditions(IConditionListener callback, boolean requested) {
+        synchronized(mMutex) {
+            if (DEBUG) Slog.d(TAG, "requestZenModeConditions callback=" + callback
+                    + " requested=" + requested);
+            if (callback == null) return;
+            if (requested) {
+                mListeners.put(callback.asBinder(), callback);
+                requestConditionsLocked(Condition.FLAG_RELEVANT_NOW);
+            } else {
+                mListeners.remove(callback.asBinder());
+                if (mListeners.isEmpty()) {
+                    requestConditionsLocked(0);
+                }
+            }
+        }
+    }
+
+    public void notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions) {
+        synchronized(mMutex) {
+            if (DEBUG) Slog.d(TAG, "notifyConditions pkg=" + pkg + " info=" + info + " conditions="
+                    + (conditions == null ? null : Arrays.asList(conditions)));
+            if (conditions == null || conditions.length == 0) return;
+            final int N = conditions.length;
+            boolean valid = true;
+            for (int i = 0; i < N; i++) {
+                final Uri id = conditions[i].id;
+                if (!Condition.isValidId(id, pkg)) {
+                    Slog.w(TAG, "Ignoring conditions from " + pkg + " for invalid id: " + id);
+                    valid = false;
+                }
+            }
+            if (!valid) return;
+
+            for (int i = 0; i < N; i++) {
+                mConditions.put(conditions[i].id, info);
+            }
+            for (IConditionListener listener : mListeners.values()) {
+                try {
+                    listener.onConditionsReceived(conditions);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Error sending conditions to listener " + listener, e);
+                }
+            }
+            if (mCurrentConditionId != null) {
+                for (int i = 0; i < N; i++) {
+                    final Condition c = conditions[i];
+                    if (!c.id.equals(mCurrentConditionId)) continue;
+                    if (c.state == Condition.STATE_TRUE || c.state == Condition.STATE_ERROR) {
+                        triggerExitLocked(c.state == Condition.STATE_ERROR);
+                        return;
+                    }
+                }
+            }
+        }
+    }
+
+    private void triggerExitLocked(boolean error) {
+        if (error) {
+            Slog.w(TAG, "Zen mode exit condition failed");
+        } else if (DEBUG) {
+            Slog.d(TAG, "Zen mode exit condition triggered");
+        }
+        mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF);
+        unsubscribeLocked(mCurrentConditionId);
+        mCurrentConditionId = null;
+    }
+
+    public void setZenModeCondition(Uri conditionId) {
+        synchronized(mMutex) {
+            if (DEBUG) Slog.d(TAG, "setZenModeCondition " + conditionId);
+            if (Objects.equal(mCurrentConditionId, conditionId)) return;
+
+            if (mCurrentConditionId != null) {
+                unsubscribeLocked(mCurrentConditionId);
+            }
+            if (conditionId != null) {
+                final ManagedServiceInfo info = mConditions.get(conditionId);
+                final IConditionProvider provider = provider(info);
+                if (provider == null) return;
+                try {
+                    provider.onSubscribe(conditionId);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Error subscribing to " + conditionId
+                            + " from " + info.component, e);
+                }
+            }
+            mCurrentConditionId = conditionId;
+        }
+    }
+
+    private void unsubscribeLocked(Uri conditionId) {
+        final ManagedServiceInfo info = mConditions.get(mCurrentConditionId);
+        final IConditionProvider provider = provider(info);
+        if (provider == null) return;
+        try {
+            provider.onUnsubscribe(conditionId);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Error unsubscribing to " + conditionId + " from " + info.component, e);
+        }
+    }
+
+    private static IConditionProvider provider(ManagedServiceInfo info) {
+        return info == null ? null : (IConditionProvider) info.service;
+    }
+
+    private void requestConditionsLocked(int flags) {
+        for (ManagedServiceInfo info : mServices) {
+            final IConditionProvider provider = provider(info);
+            if (provider == null) continue;
+            try {
+                provider.onRequestConditions(flags);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Error requesting conditions from " + info.component, e);
+            }
+        }
     }
 }