ConditionProviders.java revision 1c923a386ee4d8c31cc289f8628b8fc46bf08e86
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        for (int i = mConditions.size() - 1; i >= 0; i--) {
112            if (removed.equals(mConditions.valueAt(i))) {
113                mConditions.removeAt(i);
114            }
115        }
116    }
117
118    public ManagedServiceInfo checkServiceToken(IConditionProvider provider) {
119        synchronized(mMutex) {
120            return checkServiceTokenLocked(provider);
121        }
122    }
123
124    public void requestZenModeConditions(IConditionListener callback, boolean requested) {
125        synchronized(mMutex) {
126            if (DEBUG) Slog.d(TAG, "requestZenModeConditions callback=" + callback
127                    + " requested=" + requested);
128            if (callback == null) return;
129            if (requested) {
130                mListeners.put(callback.asBinder(), callback);
131                requestConditionsLocked(Condition.FLAG_RELEVANT_NOW);
132            } else {
133                mListeners.remove(callback.asBinder());
134                if (mListeners.isEmpty()) {
135                    requestConditionsLocked(0);
136                }
137            }
138        }
139    }
140
141    public void notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions) {
142        synchronized(mMutex) {
143            if (DEBUG) Slog.d(TAG, "notifyConditions pkg=" + pkg + " info=" + info + " conditions="
144                    + (conditions == null ? null : Arrays.asList(conditions)));
145            if (conditions == null || conditions.length == 0) return;
146            final int N = conditions.length;
147            boolean valid = true;
148            for (int i = 0; i < N; i++) {
149                final Uri id = conditions[i].id;
150                if (!Condition.isValidId(id, pkg)) {
151                    Slog.w(TAG, "Ignoring conditions from " + pkg + " for invalid id: " + id);
152                    valid = false;
153                }
154            }
155            if (!valid) return;
156
157            for (int i = 0; i < N; i++) {
158                mConditions.put(conditions[i].id, info);
159            }
160            for (IConditionListener listener : mListeners.values()) {
161                try {
162                    listener.onConditionsReceived(conditions);
163                } catch (RemoteException e) {
164                    Slog.w(TAG, "Error sending conditions to listener " + listener, e);
165                }
166            }
167            if (mCurrentConditionId != null) {
168                for (int i = 0; i < N; i++) {
169                    final Condition c = conditions[i];
170                    if (!c.id.equals(mCurrentConditionId)) continue;
171                    if (c.state == Condition.STATE_TRUE || c.state == Condition.STATE_ERROR) {
172                        triggerExitLocked(c.state == Condition.STATE_ERROR);
173                        return;
174                    }
175                }
176            }
177        }
178    }
179
180    private void triggerExitLocked(boolean error) {
181        if (error) {
182            Slog.w(TAG, "Zen mode exit condition failed");
183        } else if (DEBUG) {
184            Slog.d(TAG, "Zen mode exit condition triggered");
185        }
186        mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF);
187        unsubscribeLocked(mCurrentConditionId);
188        mCurrentConditionId = null;
189    }
190
191    public void setZenModeCondition(Uri conditionId) {
192        synchronized(mMutex) {
193            if (DEBUG) Slog.d(TAG, "setZenModeCondition " + conditionId);
194            if (Objects.equal(mCurrentConditionId, conditionId)) return;
195
196            if (mCurrentConditionId != null) {
197                unsubscribeLocked(mCurrentConditionId);
198            }
199            if (conditionId != null) {
200                final ManagedServiceInfo info = mConditions.get(conditionId);
201                final IConditionProvider provider = provider(info);
202                if (provider == null) return;
203                try {
204                    provider.onSubscribe(conditionId);
205                } catch (RemoteException e) {
206                    Slog.w(TAG, "Error subscribing to " + conditionId
207                            + " from " + info.component, e);
208                }
209            }
210            mCurrentConditionId = conditionId;
211        }
212    }
213
214    private void unsubscribeLocked(Uri conditionId) {
215        final ManagedServiceInfo info = mConditions.get(mCurrentConditionId);
216        final IConditionProvider provider = provider(info);
217        if (provider == null) return;
218        try {
219            provider.onUnsubscribe(conditionId);
220        } catch (RemoteException e) {
221            Slog.w(TAG, "Error unsubscribing to " + conditionId + " from " + info.component, e);
222        }
223    }
224
225    private static IConditionProvider provider(ManagedServiceInfo info) {
226        return info == null ? null : (IConditionProvider) info.service;
227    }
228
229    private void requestConditionsLocked(int flags) {
230        for (ManagedServiceInfo info : mServices) {
231            final IConditionProvider provider = provider(info);
232            if (provider == null) continue;
233            try {
234                provider.onRequestConditions(flags);
235            } catch (RemoteException e) {
236                Slog.w(TAG, "Error requesting conditions from " + info.component, e);
237            }
238        }
239    }
240
241    private class ZenModeHelperCallback extends ZenModeHelper.Callback {
242        @Override
243        void onZenModeChanged() {
244            final int mode = mZenModeHelper.getZenMode();
245            if (mode == Global.ZEN_MODE_OFF) {
246                synchronized (mMutex) {
247                    if (mCurrentConditionId != null) {
248                        if (DEBUG) Slog.d(TAG, "Zen mode off, forcing unsubscribe from "
249                                + mCurrentConditionId);
250                        unsubscribeLocked(mCurrentConditionId);
251                        mCurrentConditionId = null;
252                    }
253                }
254            }
255        }
256    }
257}
258