OSDN Git Service

DO NOT MERGE. Grant MMS Uri permissions as the calling UID.
[android-x86/frameworks-base.git] / services / core / java / com / android / server / notification / ConditionProviders.java
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
17 package com.android.server.notification;
18
19 import android.annotation.NonNull;
20 import android.app.INotificationManager;
21 import android.app.NotificationManager;
22 import android.content.ComponentName;
23 import android.content.ContentResolver;
24 import android.content.Context;
25 import android.net.Uri;
26 import android.os.Handler;
27 import android.os.IBinder;
28 import android.os.IInterface;
29 import android.os.RemoteException;
30 import android.os.UserHandle;
31 import android.provider.Settings;
32 import android.service.notification.Condition;
33 import android.service.notification.ConditionProviderService;
34 import android.service.notification.IConditionProvider;
35 import android.text.TextUtils;
36 import android.util.ArrayMap;
37 import android.util.ArraySet;
38 import android.util.Slog;
39
40 import com.android.internal.R;
41 import com.android.server.notification.NotificationManagerService.DumpFilter;
42
43 import java.io.PrintWriter;
44 import java.util.ArrayList;
45 import java.util.Arrays;
46
47 public class ConditionProviders extends ManagedServices {
48     private final ArrayList<ConditionRecord> mRecords = new ArrayList<>();
49     private final ArraySet<String> mSystemConditionProviderNames;
50     private final ArraySet<SystemConditionProviderService> mSystemConditionProviders
51             = new ArraySet<>();
52
53     private Callback mCallback;
54
55     public ConditionProviders(Context context, Handler handler, UserProfiles userProfiles) {
56         super(context, handler, new Object(), userProfiles);
57         mSystemConditionProviderNames = safeSet(PropConfig.getStringArray(mContext,
58                 "system.condition.providers",
59                 R.array.config_system_condition_providers));
60     }
61
62     public void setCallback(Callback callback) {
63         mCallback = callback;
64     }
65
66     public boolean isSystemProviderEnabled(String path) {
67         return mSystemConditionProviderNames.contains(path);
68     }
69
70     public void addSystemProvider(SystemConditionProviderService service) {
71         mSystemConditionProviders.add(service);
72         service.attachBase(mContext);
73         registerService(service.asInterface(), service.getComponent(), UserHandle.USER_SYSTEM);
74     }
75
76     public Iterable<SystemConditionProviderService> getSystemProviders() {
77         return mSystemConditionProviders;
78     }
79
80     @Override
81     protected Config getConfig() {
82         final Config c = new Config();
83         c.caption = "condition provider";
84         c.serviceInterface = ConditionProviderService.SERVICE_INTERFACE;
85         c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES;
86         c.secondarySettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
87         c.bindPermission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
88         c.settingsAction = Settings.ACTION_CONDITION_PROVIDER_SETTINGS;
89         c.clientLabel = R.string.condition_provider_service_binding_label;
90         return c;
91     }
92
93     @Override
94     public void dump(PrintWriter pw, DumpFilter filter) {
95         super.dump(pw, filter);
96         synchronized(mMutex) {
97             pw.print("    mRecords("); pw.print(mRecords.size()); pw.println("):");
98             for (int i = 0; i < mRecords.size(); i++) {
99                 final ConditionRecord r = mRecords.get(i);
100                 if (filter != null && !filter.matches(r.component)) continue;
101                 pw.print("      "); pw.println(r);
102                 final String countdownDesc =  CountdownConditionProvider.tryParseDescription(r.id);
103                 if (countdownDesc != null) {
104                     pw.print("        ("); pw.print(countdownDesc); pw.println(")");
105                 }
106             }
107         }
108         pw.print("    mSystemConditionProviders: "); pw.println(mSystemConditionProviderNames);
109         for (int i = 0; i < mSystemConditionProviders.size(); i++) {
110             mSystemConditionProviders.valueAt(i).dump(pw, filter);
111         }
112     }
113
114     @Override
115     protected IInterface asInterface(IBinder binder) {
116         return IConditionProvider.Stub.asInterface(binder);
117     }
118
119     @Override
120     protected boolean checkType(IInterface service) {
121         return service instanceof IConditionProvider;
122     }
123
124     @Override
125     public void onBootPhaseAppsCanStart() {
126         super.onBootPhaseAppsCanStart();
127         for (int i = 0; i < mSystemConditionProviders.size(); i++) {
128             mSystemConditionProviders.valueAt(i).onBootComplete();
129         }
130         if (mCallback != null) {
131             mCallback.onBootComplete();
132         }
133     }
134
135     @Override
136     public void onUserSwitched(int user) {
137         super.onUserSwitched(user);
138         if (mCallback != null) {
139             mCallback.onUserSwitched();
140         }
141     }
142
143     @Override
144     protected void onServiceAdded(ManagedServiceInfo info) {
145         final IConditionProvider provider = provider(info);
146         try {
147             provider.onConnected();
148         } catch (RemoteException e) {
149             // we tried
150         }
151         if (mCallback != null) {
152             mCallback.onServiceAdded(info.component);
153         }
154     }
155
156     @Override
157     protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
158         if (removed == null) return;
159         for (int i = mRecords.size() - 1; i >= 0; i--) {
160             final ConditionRecord r = mRecords.get(i);
161             if (!r.component.equals(removed.component)) continue;
162             mRecords.remove(i);
163         }
164     }
165
166     @Override
167     public void onPackagesChanged(boolean removingPackage, String[] pkgList) {
168         if (removingPackage) {
169             INotificationManager inm = NotificationManager.getService();
170
171             if (pkgList != null && (pkgList.length > 0)) {
172                 for (String pkgName : pkgList) {
173                     try {
174                         inm.removeAutomaticZenRules(pkgName);
175                         inm.setNotificationPolicyAccessGranted(pkgName, false);
176                     } catch (Exception e) {
177                         Slog.e(TAG, "Failed to clean up rules for " + pkgName, e);
178                     }
179                 }
180             }
181         }
182         super.onPackagesChanged(removingPackage, pkgList);
183     }
184
185     public ManagedServiceInfo checkServiceToken(IConditionProvider provider) {
186         synchronized(mMutex) {
187             return checkServiceTokenLocked(provider);
188         }
189     }
190
191     private Condition[] removeDuplicateConditions(String pkg, Condition[] conditions) {
192         if (conditions == null || conditions.length == 0) return null;
193         final int N = conditions.length;
194         final ArrayMap<Uri, Condition> valid = new ArrayMap<Uri, Condition>(N);
195         for (int i = 0; i < N; i++) {
196             final Uri id = conditions[i].id;
197             if (valid.containsKey(id)) {
198                 Slog.w(TAG, "Ignoring condition from " + pkg + " for duplicate id: " + id);
199                 continue;
200             }
201             valid.put(id, conditions[i]);
202         }
203         if (valid.size() == 0) return null;
204         if (valid.size() == N) return conditions;
205         final Condition[] rt = new Condition[valid.size()];
206         for (int i = 0; i < rt.length; i++) {
207             rt[i] = valid.valueAt(i);
208         }
209         return rt;
210     }
211
212     private ConditionRecord getRecordLocked(Uri id, ComponentName component, boolean create) {
213         if (id == null || component == null) return null;
214         final int N = mRecords.size();
215         for (int i = 0; i < N; i++) {
216             final ConditionRecord r = mRecords.get(i);
217             if (r.id.equals(id) && r.component.equals(component)) {
218                 return r;
219             }
220         }
221         if (create) {
222             final ConditionRecord r = new ConditionRecord(id, component);
223             mRecords.add(r);
224             return r;
225         }
226         return null;
227     }
228
229     public void notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions) {
230         synchronized(mMutex) {
231             if (DEBUG) Slog.d(TAG, "notifyConditions pkg=" + pkg + " info=" + info + " conditions="
232                     + (conditions == null ? null : Arrays.asList(conditions)));
233             conditions = removeDuplicateConditions(pkg, conditions);
234             if (conditions == null || conditions.length == 0) return;
235             final int N = conditions.length;
236             for (int i = 0; i < N; i++) {
237                 final Condition c = conditions[i];
238                 final ConditionRecord r = getRecordLocked(c.id, info.component, true /*create*/);
239                 r.info = info;
240                 r.condition = c;
241             }
242         }
243         final int N = conditions.length;
244         for (int i = 0; i < N; i++) {
245             final Condition c = conditions[i];
246             if (mCallback != null) {
247                 mCallback.onConditionChanged(c.id, c);
248             }
249         }
250     }
251
252     public IConditionProvider findConditionProvider(ComponentName component) {
253         if (component == null) return null;
254         for (ManagedServiceInfo service : mServices) {
255             if (component.equals(service.component)) {
256                 return provider(service);
257             }
258         }
259         return null;
260     }
261
262     public Condition findCondition(ComponentName component, Uri conditionId) {
263         if (component == null || conditionId == null) return null;
264         synchronized (mMutex) {
265             final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/);
266             return r != null ? r.condition : null;
267         }
268     }
269
270     public void ensureRecordExists(ComponentName component, Uri conditionId,
271             IConditionProvider provider) {
272         // constructed by convention, make sure the record exists...
273         final ConditionRecord r = getRecordLocked(conditionId, component, true /*create*/);
274         if (r.info == null) {
275             // ... and is associated with the in-process service
276             r.info = checkServiceTokenLocked(provider);
277         }
278     }
279
280     @Override
281     protected @NonNull ArraySet<ComponentName> loadComponentNamesFromSetting(String settingName,
282             int userId) {
283         final ContentResolver cr = mContext.getContentResolver();
284         String settingValue = Settings.Secure.getStringForUser(
285                 cr,
286                 settingName,
287                 userId);
288         if (TextUtils.isEmpty(settingValue))
289             return new ArraySet<>();
290         String[] packages = settingValue.split(ENABLED_SERVICES_SEPARATOR);
291         ArraySet<ComponentName> result = new ArraySet<>(packages.length);
292         for (int i = 0; i < packages.length; i++) {
293             if (!TextUtils.isEmpty(packages[i])) {
294                 final ComponentName component = ComponentName.unflattenFromString(packages[i]);
295                 if (component != null) {
296                     result.addAll(queryPackageForServices(component.getPackageName(), userId));
297                 } else {
298                     result.addAll(queryPackageForServices(packages[i], userId));
299                 }
300             }
301         }
302         return result;
303     }
304
305     public boolean subscribeIfNecessary(ComponentName component, Uri conditionId) {
306         synchronized (mMutex) {
307             final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/);
308             if (r == null) {
309                 Slog.w(TAG, "Unable to subscribe to " + component + " " + conditionId);
310                 return false;
311             }
312             if (r.subscribed) return true;
313             subscribeLocked(r);
314             return r.subscribed;
315         }
316     }
317
318     public void unsubscribeIfNecessary(ComponentName component, Uri conditionId) {
319         synchronized (mMutex) {
320             final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/);
321             if (r == null) {
322                 Slog.w(TAG, "Unable to unsubscribe to " + component + " " + conditionId);
323                 return;
324             }
325             if (!r.subscribed) return;
326             unsubscribeLocked(r);;
327         }
328     }
329
330     private void subscribeLocked(ConditionRecord r) {
331         if (DEBUG) Slog.d(TAG, "subscribeLocked " + r);
332         final IConditionProvider provider = provider(r);
333         RemoteException re = null;
334         if (provider != null) {
335             try {
336                 Slog.d(TAG, "Subscribing to " + r.id + " with " + r.component);
337                 provider.onSubscribe(r.id);
338                 r.subscribed = true;
339             } catch (RemoteException e) {
340                 Slog.w(TAG, "Error subscribing to " + r, e);
341                 re = e;
342             }
343         }
344         ZenLog.traceSubscribe(r != null ? r.id : null, provider, re);
345     }
346
347     @SafeVarargs
348     private static <T> ArraySet<T> safeSet(T... items) {
349         final ArraySet<T> rt = new ArraySet<T>();
350         if (items == null || items.length == 0) return rt;
351         final int N = items.length;
352         for (int i = 0; i < N; i++) {
353             final T item = items[i];
354             if (item != null) {
355                 rt.add(item);
356             }
357         }
358         return rt;
359     }
360
361     private void unsubscribeLocked(ConditionRecord r) {
362         if (DEBUG) Slog.d(TAG, "unsubscribeLocked " + r);
363         final IConditionProvider provider = provider(r);
364         RemoteException re = null;
365         if (provider != null) {
366             try {
367                 provider.onUnsubscribe(r.id);
368             } catch (RemoteException e) {
369                 Slog.w(TAG, "Error unsubscribing to " + r, e);
370                 re = e;
371             }
372             r.subscribed = false;
373         }
374         ZenLog.traceUnsubscribe(r != null ? r.id : null, provider, re);
375     }
376
377     private static IConditionProvider provider(ConditionRecord r) {
378         return r == null ? null : provider(r.info);
379     }
380
381     private static IConditionProvider provider(ManagedServiceInfo info) {
382         return info == null ? null : (IConditionProvider) info.service;
383     }
384
385     private static class ConditionRecord {
386         public final Uri id;
387         public final ComponentName component;
388         public Condition condition;
389         public ManagedServiceInfo info;
390         public boolean subscribed;
391
392         private ConditionRecord(Uri id, ComponentName component) {
393             this.id = id;
394             this.component = component;
395         }
396
397         @Override
398         public String toString() {
399             final StringBuilder sb = new StringBuilder("ConditionRecord[id=")
400                     .append(id).append(",component=").append(component)
401                     .append(",subscribed=").append(subscribed);
402             return sb.append(']').toString();
403         }
404     }
405
406     public interface Callback {
407         void onBootComplete();
408         void onServiceAdded(ComponentName component);
409         void onConditionChanged(Uri id, Condition condition);
410         void onUserSwitched();
411     }
412
413 }