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