ConditionProviders.java revision e86de4c0670550a29edae77ebb9f5c8ba5631231
1/**
2 * Copyright (c) 2014, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.notification;
18
19import android.content.Context;
20import android.net.Uri;
21import android.os.Handler;
22import android.os.IBinder;
23import android.os.IInterface;
24import android.os.RemoteException;
25import android.provider.Settings;
26import android.provider.Settings.Global;
27import android.service.notification.Condition;
28import android.service.notification.ConditionProviderService;
29import android.service.notification.IConditionListener;
30import android.service.notification.IConditionProvider;
31import android.util.ArrayMap;
32import android.util.Slog;
33
34import com.android.internal.R;
35
36import libcore.util.Objects;
37
38import java.io.PrintWriter;
39import java.util.Arrays;
40
41public class ConditionProviders extends ManagedServices {
42
43    private final ZenModeHelper mZenModeHelper;
44    private final ArrayMap<IBinder, IConditionListener> mListeners
45            = new ArrayMap<IBinder, IConditionListener>();
46    private final ArrayMap<Uri, ManagedServiceInfo> mConditions
47            = new ArrayMap<Uri, ManagedServiceInfo>();
48
49    private Uri mCurrentConditionId;
50
51    public ConditionProviders(Context context, Handler handler,
52            UserProfiles userProfiles, ZenModeHelper zenModeHelper) {
53        super(context, handler, new Object(), userProfiles);
54        mZenModeHelper = zenModeHelper;
55        mZenModeHelper.addCallback(new ZenModeHelperCallback());
56    }
57
58    @Override
59    protected Config getConfig() {
60        Config c = new Config();
61        c.caption = "condition provider";
62        c.serviceInterface = ConditionProviderService.SERVICE_INTERFACE;
63        c.secureSettingName = Settings.Secure.ENABLED_CONDITION_PROVIDERS;
64        c.bindPermission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
65        c.settingsAction = Settings.ACTION_CONDITION_PROVIDER_SETTINGS;
66        c.clientLabel = R.string.condition_provider_service_binding_label;
67        return c;
68    }
69
70    @Override
71    public void dump(PrintWriter pw) {
72        super.dump(pw);
73        synchronized(mMutex) {
74            pw.print("    mCurrentConditionId="); pw.println(mCurrentConditionId);
75            pw.print("    mListeners("); pw.print(mListeners.size()); pw.println("):");
76            for (int i = 0; i < mListeners.size(); i++) {
77                pw.print("      "); pw.println(mListeners.keyAt(i));
78            }
79            pw.print("    mConditions("); pw.print(mConditions.size()); pw.println("):");
80            for (int i = 0; i < mConditions.size(); i++) {
81                pw.print("      "); pw.print(mConditions.keyAt(i));
82                final ManagedServiceInfo info = mConditions.valueAt(i);
83                pw.print(" -> "); pw.print(info.component);
84                if (!mServices.contains(info)) {
85                    pw.print(" (orphan)");
86                }
87                pw.println();
88            }
89        }
90    }
91
92    @Override
93    protected IInterface asInterface(IBinder binder) {
94        return IConditionProvider.Stub.asInterface(binder);
95    }
96
97    @Override
98    protected void onServiceAdded(IInterface service) {
99        Slog.d(TAG, "onServiceAdded " + service);
100        final IConditionProvider provider = (IConditionProvider) service;
101        try {
102            provider.onConnected();
103        } catch (RemoteException e) {
104            // we tried
105        }
106    }
107
108    @Override
109    protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
110        if (removed == null) return;
111        if (mCurrentConditionId != null) {
112            if (removed.equals(mConditions.get(mCurrentConditionId))) {
113                mCurrentConditionId = null;
114                mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF);
115            }
116        }
117        for (int i = mConditions.size() - 1; i >= 0; i--) {
118            if (removed.equals(mConditions.valueAt(i))) {
119                mConditions.removeAt(i);
120            }
121        }
122    }
123
124    public ManagedServiceInfo checkServiceToken(IConditionProvider provider) {
125        synchronized(mMutex) {
126            return checkServiceTokenLocked(provider);
127        }
128    }
129
130    public void requestZenModeConditions(IConditionListener callback, boolean requested) {
131        synchronized(mMutex) {
132            if (DEBUG) Slog.d(TAG, "requestZenModeConditions callback=" + callback
133                    + " requested=" + requested);
134            if (callback == null) return;
135            if (requested) {
136                mListeners.put(callback.asBinder(), callback);
137                requestConditionsLocked(Condition.FLAG_RELEVANT_NOW);
138            } else {
139                mListeners.remove(callback.asBinder());
140                if (mListeners.isEmpty()) {
141                    requestConditionsLocked(0);
142                }
143            }
144        }
145    }
146
147    public void notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions) {
148        synchronized(mMutex) {
149            if (DEBUG) Slog.d(TAG, "notifyConditions pkg=" + pkg + " info=" + info + " conditions="
150                    + (conditions == null ? null : Arrays.asList(conditions)));
151            if (conditions == null || conditions.length == 0) return;
152            final int N = conditions.length;
153            boolean valid = true;
154            for (int i = 0; i < N; i++) {
155                final Uri id = conditions[i].id;
156                if (!Condition.isValidId(id, pkg)) {
157                    Slog.w(TAG, "Ignoring conditions from " + pkg + " for invalid id: " + id);
158                    valid = false;
159                }
160            }
161            if (!valid) return;
162
163            for (int i = 0; i < N; i++) {
164                mConditions.put(conditions[i].id, info);
165            }
166            for (IConditionListener listener : mListeners.values()) {
167                try {
168                    listener.onConditionsReceived(conditions);
169                } catch (RemoteException e) {
170                    Slog.w(TAG, "Error sending conditions to listener " + listener, e);
171                }
172            }
173            if (mCurrentConditionId != null) {
174                for (int i = 0; i < N; i++) {
175                    final Condition c = conditions[i];
176                    if (!c.id.equals(mCurrentConditionId)) continue;
177                    if (c.state == Condition.STATE_TRUE || c.state == Condition.STATE_ERROR) {
178                        triggerExitLocked(c.state == Condition.STATE_ERROR);
179                        return;
180                    }
181                }
182            }
183        }
184    }
185
186    private void triggerExitLocked(boolean error) {
187        if (error) {
188            Slog.w(TAG, "Zen mode exit condition failed");
189        } else if (DEBUG) {
190            Slog.d(TAG, "Zen mode exit condition triggered");
191        }
192        mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF);
193        unsubscribeLocked(mCurrentConditionId);
194        mCurrentConditionId = null;
195    }
196
197    public void setZenModeCondition(Uri conditionId) {
198        synchronized(mMutex) {
199            if (DEBUG) Slog.d(TAG, "setZenModeCondition " + conditionId);
200            if (Objects.equal(mCurrentConditionId, conditionId)) return;
201
202            if (mCurrentConditionId != null) {
203                unsubscribeLocked(mCurrentConditionId);
204            }
205            if (conditionId != null) {
206                final ManagedServiceInfo info = mConditions.get(conditionId);
207                final IConditionProvider provider = provider(info);
208                if (provider == null) return;
209                try {
210                    provider.onSubscribe(conditionId);
211                } catch (RemoteException e) {
212                    Slog.w(TAG, "Error subscribing to " + conditionId
213                            + " from " + info.component, e);
214                }
215            }
216            mCurrentConditionId = conditionId;
217        }
218    }
219
220    private void unsubscribeLocked(Uri conditionId) {
221        final ManagedServiceInfo info = mConditions.get(mCurrentConditionId);
222        final IConditionProvider provider = provider(info);
223        if (provider == null) return;
224        try {
225            provider.onUnsubscribe(conditionId);
226        } catch (RemoteException e) {
227            Slog.w(TAG, "Error unsubscribing to " + conditionId + " from " + info.component, e);
228        }
229    }
230
231    private static IConditionProvider provider(ManagedServiceInfo info) {
232        return info == null ? null : (IConditionProvider) info.service;
233    }
234
235    private void requestConditionsLocked(int flags) {
236        for (ManagedServiceInfo info : mServices) {
237            final IConditionProvider provider = provider(info);
238            if (provider == null) continue;
239            try {
240                provider.onRequestConditions(flags);
241            } catch (RemoteException e) {
242                Slog.w(TAG, "Error requesting conditions from " + info.component, e);
243            }
244        }
245    }
246
247    private class ZenModeHelperCallback extends ZenModeHelper.Callback {
248        @Override
249        void onZenModeChanged() {
250            final int mode = mZenModeHelper.getZenMode();
251            if (mode == Global.ZEN_MODE_OFF) {
252                synchronized (mMutex) {
253                    if (mCurrentConditionId != null) {
254                        if (DEBUG) Slog.d(TAG, "Zen mode off, forcing unsubscribe from "
255                                + mCurrentConditionId);
256                        unsubscribeLocked(mCurrentConditionId);
257                        mCurrentConditionId = null;
258                    }
259                }
260            }
261        }
262    }
263}
264