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 // we tried 154 } 155 if (mCallback != null) { 156 mCallback.onServiceAdded(info.component); 157 } 158 } 159 160 @Override onServiceRemovedLocked(ManagedServiceInfo removed)161 protected void onServiceRemovedLocked(ManagedServiceInfo removed) { 162 if (removed == null) return; 163 for (int i = mRecords.size() - 1; i >= 0; i--) { 164 final ConditionRecord r = mRecords.get(i); 165 if (!r.component.equals(removed.component)) continue; 166 mRecords.remove(i); 167 } 168 } 169 170 @Override onPackagesChanged(boolean removingPackage, String[] pkgList, int[] uid)171 public void onPackagesChanged(boolean removingPackage, String[] pkgList, int[] uid) { 172 if (removingPackage) { 173 INotificationManager inm = NotificationManager.getService(); 174 175 if (pkgList != null && (pkgList.length > 0)) { 176 for (String pkgName : pkgList) { 177 try { 178 inm.removeAutomaticZenRules(pkgName); 179 inm.setNotificationPolicyAccessGranted(pkgName, false); 180 } catch (Exception e) { 181 Slog.e(TAG, "Failed to clean up rules for " + pkgName, e); 182 } 183 } 184 } 185 } 186 super.onPackagesChanged(removingPackage, pkgList, uid); 187 } 188 189 @Override isValidEntry(String packageOrComponent, int userId)190 protected boolean isValidEntry(String packageOrComponent, int userId) { 191 return true; 192 } 193 checkServiceToken(IConditionProvider provider)194 public ManagedServiceInfo checkServiceToken(IConditionProvider provider) { 195 synchronized(mMutex) { 196 return checkServiceTokenLocked(provider); 197 } 198 } 199 removeDuplicateConditions(String pkg, Condition[] conditions)200 private Condition[] removeDuplicateConditions(String pkg, Condition[] conditions) { 201 if (conditions == null || conditions.length == 0) return null; 202 final int N = conditions.length; 203 final ArrayMap<Uri, Condition> valid = new ArrayMap<Uri, Condition>(N); 204 for (int i = 0; i < N; i++) { 205 final Uri id = conditions[i].id; 206 if (valid.containsKey(id)) { 207 Slog.w(TAG, "Ignoring condition from " + pkg + " for duplicate id: " + id); 208 continue; 209 } 210 valid.put(id, conditions[i]); 211 } 212 if (valid.size() == 0) return null; 213 if (valid.size() == N) return conditions; 214 final Condition[] rt = new Condition[valid.size()]; 215 for (int i = 0; i < rt.length; i++) { 216 rt[i] = valid.valueAt(i); 217 } 218 return rt; 219 } 220 getRecordLocked(Uri id, ComponentName component, boolean create)221 private ConditionRecord getRecordLocked(Uri id, ComponentName component, boolean create) { 222 if (id == null || component == null) return null; 223 final int N = mRecords.size(); 224 for (int i = 0; i < N; i++) { 225 final ConditionRecord r = mRecords.get(i); 226 if (r.id.equals(id) && r.component.equals(component)) { 227 return r; 228 } 229 } 230 if (create) { 231 final ConditionRecord r = new ConditionRecord(id, component); 232 mRecords.add(r); 233 return r; 234 } 235 return null; 236 } 237 notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions)238 public void notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions) { 239 synchronized(mMutex) { 240 if (DEBUG) Slog.d(TAG, "notifyConditions pkg=" + pkg + " info=" + info + " conditions=" 241 + (conditions == null ? null : Arrays.asList(conditions))); 242 conditions = removeDuplicateConditions(pkg, conditions); 243 if (conditions == null || conditions.length == 0) return; 244 final int N = conditions.length; 245 for (int i = 0; i < N; i++) { 246 final Condition c = conditions[i]; 247 final ConditionRecord r = getRecordLocked(c.id, info.component, true /*create*/); 248 r.info = info; 249 r.condition = c; 250 } 251 } 252 final int N = conditions.length; 253 for (int i = 0; i < N; i++) { 254 final Condition c = conditions[i]; 255 if (mCallback != null) { 256 mCallback.onConditionChanged(c.id, c); 257 } 258 } 259 } 260 findConditionProvider(ComponentName component)261 public IConditionProvider findConditionProvider(ComponentName component) { 262 if (component == null) return null; 263 for (ManagedServiceInfo service : getServices()) { 264 if (component.equals(service.component)) { 265 return provider(service); 266 } 267 } 268 return null; 269 } 270 findCondition(ComponentName component, Uri conditionId)271 public Condition findCondition(ComponentName component, Uri conditionId) { 272 if (component == null || conditionId == null) return null; 273 synchronized (mMutex) { 274 final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/); 275 return r != null ? r.condition : null; 276 } 277 } 278 ensureRecordExists(ComponentName component, Uri conditionId, IConditionProvider provider)279 public void ensureRecordExists(ComponentName component, Uri conditionId, 280 IConditionProvider provider) { 281 // constructed by convention, make sure the record exists... 282 final ConditionRecord r = getRecordLocked(conditionId, component, true /*create*/); 283 if (r.info == null) { 284 // ... and is associated with the in-process service 285 r.info = checkServiceTokenLocked(provider); 286 } 287 } 288 subscribeIfNecessary(ComponentName component, Uri conditionId)289 public boolean subscribeIfNecessary(ComponentName component, Uri conditionId) { 290 synchronized (mMutex) { 291 final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/); 292 if (r == null) { 293 Slog.w(TAG, "Unable to subscribe to " + component + " " + conditionId); 294 return false; 295 } 296 if (r.subscribed) return true; 297 subscribeLocked(r); 298 return r.subscribed; 299 } 300 } 301 unsubscribeIfNecessary(ComponentName component, Uri conditionId)302 public void unsubscribeIfNecessary(ComponentName component, Uri conditionId) { 303 synchronized (mMutex) { 304 final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/); 305 if (r == null) { 306 Slog.w(TAG, "Unable to unsubscribe to " + component + " " + conditionId); 307 return; 308 } 309 if (!r.subscribed) return; 310 unsubscribeLocked(r);; 311 } 312 } 313 subscribeLocked(ConditionRecord r)314 private void subscribeLocked(ConditionRecord r) { 315 if (DEBUG) Slog.d(TAG, "subscribeLocked " + r); 316 final IConditionProvider provider = provider(r); 317 RemoteException re = null; 318 if (provider != null) { 319 try { 320 Slog.d(TAG, "Subscribing to " + r.id + " with " + r.component); 321 provider.onSubscribe(r.id); 322 r.subscribed = true; 323 } catch (RemoteException e) { 324 Slog.w(TAG, "Error subscribing to " + r, e); 325 re = e; 326 } 327 } 328 ZenLog.traceSubscribe(r != null ? r.id : null, provider, re); 329 } 330 331 @SafeVarargs safeSet(T... items)332 private static <T> ArraySet<T> safeSet(T... items) { 333 final ArraySet<T> rt = new ArraySet<T>(); 334 if (items == null || items.length == 0) return rt; 335 final int N = items.length; 336 for (int i = 0; i < N; i++) { 337 final T item = items[i]; 338 if (item != null) { 339 rt.add(item); 340 } 341 } 342 return rt; 343 } 344 unsubscribeLocked(ConditionRecord r)345 private void unsubscribeLocked(ConditionRecord r) { 346 if (DEBUG) Slog.d(TAG, "unsubscribeLocked " + r); 347 final IConditionProvider provider = provider(r); 348 RemoteException re = null; 349 if (provider != null) { 350 try { 351 provider.onUnsubscribe(r.id); 352 } catch (RemoteException e) { 353 Slog.w(TAG, "Error unsubscribing to " + r, e); 354 re = e; 355 } 356 r.subscribed = false; 357 } 358 ZenLog.traceUnsubscribe(r != null ? r.id : null, provider, re); 359 } 360 provider(ConditionRecord r)361 private static IConditionProvider provider(ConditionRecord r) { 362 return r == null ? null : provider(r.info); 363 } 364 provider(ManagedServiceInfo info)365 private static IConditionProvider provider(ManagedServiceInfo info) { 366 return info == null ? null : (IConditionProvider) info.service; 367 } 368 369 private static class ConditionRecord { 370 public final Uri id; 371 public final ComponentName component; 372 public Condition condition; 373 public ManagedServiceInfo info; 374 public boolean subscribed; 375 ConditionRecord(Uri id, ComponentName component)376 private ConditionRecord(Uri id, ComponentName component) { 377 this.id = id; 378 this.component = component; 379 } 380 381 @Override toString()382 public String toString() { 383 final StringBuilder sb = new StringBuilder("ConditionRecord[id=") 384 .append(id).append(",component=").append(component) 385 .append(",subscribed=").append(subscribed); 386 return sb.append(']').toString(); 387 } 388 } 389 390 public interface Callback { onBootComplete()391 void onBootComplete(); onServiceAdded(ComponentName component)392 void onServiceAdded(ComponentName component); onConditionChanged(Uri id, Condition condition)393 void onConditionChanged(Uri id, Condition condition); onUserSwitched()394 void onUserSwitched(); 395 } 396 397 } 398