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