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 static android.service.notification.Condition.STATE_TRUE; 20 import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_USER; 21 22 import android.app.INotificationManager; 23 import android.app.NotificationManager; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.pm.IPackageManager; 27 import android.content.pm.ServiceInfo; 28 import android.net.Uri; 29 import android.os.IBinder; 30 import android.os.IInterface; 31 import android.os.Process; 32 import android.os.RemoteException; 33 import android.os.UserHandle; 34 import android.provider.Settings; 35 import android.service.notification.Condition; 36 import android.service.notification.ConditionProviderService; 37 import android.service.notification.IConditionProvider; 38 import android.text.TextUtils; 39 import android.util.ArrayMap; 40 import android.util.ArraySet; 41 import android.util.Slog; 42 43 import com.android.internal.R; 44 import com.android.internal.annotations.VisibleForTesting; 45 import com.android.modules.utils.TypedXmlSerializer; 46 import com.android.server.notification.NotificationManagerService.DumpFilter; 47 48 import java.io.IOException; 49 import java.io.PrintWriter; 50 import java.util.ArrayList; 51 import java.util.Arrays; 52 53 public class ConditionProviders extends ManagedServices { 54 55 @VisibleForTesting 56 static final String TAG_ENABLED_DND_APPS = "dnd_apps"; 57 58 private final ArrayList<ConditionRecord> mRecords = new ArrayList<>(); 59 private final ArraySet<String> mSystemConditionProviderNames; 60 private final ArraySet<SystemConditionProviderService> mSystemConditionProviders 61 = new ArraySet<>(); 62 private Callback mCallback; 63 ConditionProviders(Context context, UserProfiles userProfiles, IPackageManager pm)64 public ConditionProviders(Context context, UserProfiles userProfiles, IPackageManager pm) { 65 super(context, new Object(), userProfiles, pm); 66 mSystemConditionProviderNames = safeSet(PropConfig.getStringArray(mContext, 67 "system.condition.providers", 68 R.array.config_system_condition_providers)); 69 mApprovalLevel = APPROVAL_BY_PACKAGE; 70 } 71 setCallback(Callback callback)72 public void setCallback(Callback callback) { 73 mCallback = callback; 74 } 75 isSystemProviderEnabled(String path)76 public boolean isSystemProviderEnabled(String path) { 77 return mSystemConditionProviderNames.contains(path); 78 } 79 addSystemProvider(SystemConditionProviderService service)80 public void addSystemProvider(SystemConditionProviderService service) { 81 mSystemConditionProviders.add(service); 82 service.attachBase(mContext); 83 registerSystemService(service.asInterface(), service.getComponent(), UserHandle.USER_SYSTEM, 84 Process.SYSTEM_UID); 85 } 86 getSystemProviders()87 public Iterable<SystemConditionProviderService> getSystemProviders() { 88 return mSystemConditionProviders; 89 } 90 91 @Override 92 protected ArrayMap<Boolean, ArrayList<ComponentName>> resetComponents(String packageName, int userId)93 resetComponents(String packageName, int userId) { 94 resetPackage(packageName, userId); 95 ArrayMap<Boolean, ArrayList<ComponentName>> changes = new ArrayMap<>(); 96 changes.put(true, new ArrayList<>(0)); 97 changes.put(false, new ArrayList<>(0)); 98 return changes; 99 } 100 101 /** 102 * @return true if the passed package is enabled. false otherwise 103 */ resetPackage(String packageName, int userId)104 boolean resetPackage(String packageName, int userId) { 105 boolean isAllowed = super.isPackageOrComponentAllowed(packageName, userId); 106 boolean isDefault = super.isDefaultComponentOrPackage(packageName); 107 if (!isAllowed && isDefault) { 108 setPackageOrComponentEnabled(packageName, userId, true, true); 109 } 110 if (isAllowed && !isDefault) { 111 setPackageOrComponentEnabled(packageName, userId, true, false); 112 } 113 return !isAllowed && isDefault; 114 } 115 116 @Override writeDefaults(TypedXmlSerializer out)117 void writeDefaults(TypedXmlSerializer out) throws IOException { 118 synchronized (mDefaultsLock) { 119 String defaults = String.join(ENABLED_SERVICES_SEPARATOR, mDefaultPackages); 120 out.attribute(null, ATT_DEFAULTS, defaults); 121 } 122 } 123 124 @Override getConfig()125 protected Config getConfig() { 126 final Config c = new Config(); 127 c.caption = "condition provider"; 128 c.serviceInterface = ConditionProviderService.SERVICE_INTERFACE; 129 c.secureSettingName = null; 130 c.xmlTag = TAG_ENABLED_DND_APPS; 131 c.secondarySettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS; 132 c.bindPermission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE; 133 c.settingsAction = Settings.ACTION_CONDITION_PROVIDER_SETTINGS; 134 c.clientLabel = R.string.condition_provider_service_binding_label; 135 return c; 136 } 137 138 @Override dump(PrintWriter pw, DumpFilter filter)139 public void dump(PrintWriter pw, DumpFilter filter) { 140 super.dump(pw, filter); 141 synchronized(mMutex) { 142 pw.print(" mRecords("); pw.print(mRecords.size()); pw.println("):"); 143 for (int i = 0; i < mRecords.size(); i++) { 144 final ConditionRecord r = mRecords.get(i); 145 if (filter != null && !filter.matches(r.component)) continue; 146 pw.print(" "); pw.println(r); 147 final String countdownDesc = CountdownConditionProvider.tryParseDescription(r.id); 148 if (countdownDesc != null) { 149 pw.print(" ("); pw.print(countdownDesc); pw.println(")"); 150 } 151 } 152 } 153 pw.print(" mSystemConditionProviders: "); pw.println(mSystemConditionProviderNames); 154 for (int i = 0; i < mSystemConditionProviders.size(); i++) { 155 mSystemConditionProviders.valueAt(i).dump(pw, filter); 156 } 157 } 158 159 @Override asInterface(IBinder binder)160 protected IInterface asInterface(IBinder binder) { 161 return IConditionProvider.Stub.asInterface(binder); 162 } 163 164 @Override checkType(IInterface service)165 protected boolean checkType(IInterface service) { 166 return service instanceof IConditionProvider; 167 } 168 169 @Override onBootPhaseAppsCanStart()170 public void onBootPhaseAppsCanStart() { 171 super.onBootPhaseAppsCanStart(); 172 for (int i = 0; i < mSystemConditionProviders.size(); i++) { 173 mSystemConditionProviders.valueAt(i).onBootComplete(); 174 } 175 if (mCallback != null) { 176 mCallback.onBootComplete(); 177 } 178 } 179 180 @Override onUserSwitched(int user)181 public void onUserSwitched(int user) { 182 super.onUserSwitched(user); 183 if (mCallback != null) { 184 mCallback.onUserSwitched(); 185 } 186 } 187 188 @Override onServiceAdded(ManagedServiceInfo info)189 protected void onServiceAdded(ManagedServiceInfo info) { 190 final IConditionProvider provider = provider(info); 191 try { 192 provider.onConnected(); 193 } catch (RemoteException e) { 194 Slog.e(TAG, "can't connect to service " + info, e); 195 // we tried 196 } 197 if (mCallback != null) { 198 mCallback.onServiceAdded(info.component); 199 } 200 } 201 202 @Override ensureFilters(ServiceInfo si, int userId)203 protected void ensureFilters(ServiceInfo si, int userId) { 204 // nothing to filter 205 } 206 207 @Override loadDefaultsFromConfig()208 protected void loadDefaultsFromConfig() { 209 String defaultDndAccess = mContext.getResources().getString( 210 R.string.config_defaultDndAccessPackages); 211 if (defaultDndAccess != null) { 212 String[] dnds = defaultDndAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR); 213 for (int i = 0; i < dnds.length; i++) { 214 if (TextUtils.isEmpty(dnds[i])) { 215 continue; 216 } 217 addDefaultComponentOrPackage(dnds[i]); 218 } 219 } 220 } 221 222 @Override onServiceRemovedLocked(ManagedServiceInfo removed)223 protected void onServiceRemovedLocked(ManagedServiceInfo removed) { 224 if (removed == null) return; 225 for (int i = mRecords.size() - 1; i >= 0; i--) { 226 final ConditionRecord r = mRecords.get(i); 227 if (!r.component.equals(removed.component)) continue; 228 mRecords.remove(i); 229 } 230 } 231 232 @Override onPackagesChanged(boolean removingPackage, String[] pkgList, int[] uid)233 public void onPackagesChanged(boolean removingPackage, String[] pkgList, int[] uid) { 234 if (removingPackage) { 235 INotificationManager inm = NotificationManager.getService(); 236 237 if (pkgList != null && (pkgList.length > 0)) { 238 for (String pkgName : pkgList) { 239 try { 240 inm.removeAutomaticZenRules(pkgName, /* fromUser= */ false); 241 inm.setNotificationPolicyAccessGranted(pkgName, false); 242 } catch (Exception e) { 243 Slog.e(TAG, "Failed to clean up rules for " + pkgName, e); 244 } 245 } 246 } 247 } 248 super.onPackagesChanged(removingPackage, pkgList, uid); 249 } 250 251 @Override isValidEntry(String packageOrComponent, int userId)252 protected boolean isValidEntry(String packageOrComponent, int userId) { 253 return true; 254 } 255 256 @Override allowRebindForParentUser()257 protected boolean allowRebindForParentUser() { 258 return true; 259 } 260 261 @Override getRequiredPermission()262 protected String getRequiredPermission() { 263 return null; 264 } 265 checkServiceToken(IConditionProvider provider)266 public ManagedServiceInfo checkServiceToken(IConditionProvider provider) { 267 synchronized(mMutex) { 268 return checkServiceTokenLocked(provider); 269 } 270 } 271 getValidConditions(String pkg, Condition[] conditions)272 private Condition[] getValidConditions(String pkg, Condition[] conditions) { 273 if (conditions == null || conditions.length == 0) return null; 274 final int N = conditions.length; 275 final ArrayMap<Uri, Condition> valid = new ArrayMap<Uri, Condition>(N); 276 for (int i = 0; i < N; i++) { 277 if (conditions[i] == null) { 278 Slog.w(TAG, "Ignoring null condition from " + pkg); 279 continue; 280 } 281 final Uri id = conditions[i].id; 282 if (valid.containsKey(id)) { 283 Slog.w(TAG, "Ignoring condition from " + pkg + " for duplicate id: " + id); 284 continue; 285 } 286 valid.put(id, conditions[i]); 287 } 288 if (valid.size() == 0) return null; 289 if (valid.size() == N) return conditions; 290 final Condition[] rt = new Condition[valid.size()]; 291 for (int i = 0; i < rt.length; i++) { 292 rt[i] = valid.valueAt(i); 293 } 294 return rt; 295 } 296 getRecordLocked(Uri id, ComponentName component, boolean create)297 private ConditionRecord getRecordLocked(Uri id, ComponentName component, boolean create) { 298 if (id == null || component == null) return null; 299 final int N = mRecords.size(); 300 for (int i = 0; i < N; i++) { 301 final ConditionRecord r = mRecords.get(i); 302 if (r.id.equals(id) && r.component.equals(component)) { 303 return r; 304 } 305 } 306 if (create) { 307 final ConditionRecord r = new ConditionRecord(id, component); 308 mRecords.add(r); 309 return r; 310 } 311 return null; 312 } 313 notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions)314 public void notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions) { 315 synchronized(mMutex) { 316 if (DEBUG) Slog.d(TAG, "notifyConditions pkg=" + pkg + " info=" + info + " conditions=" 317 + (conditions == null ? null : Arrays.asList(conditions))); 318 conditions = getValidConditions(pkg, conditions); 319 if (conditions == null || conditions.length == 0) return; 320 final int N = conditions.length; 321 for (int i = 0; i < N; i++) { 322 final Condition c = conditions[i]; 323 final ConditionRecord r = getRecordLocked(c.id, info.component, true /*create*/); 324 r.info = info; 325 if (android.app.Flags.modesUi()) { 326 // if user turned on the mode, ignore the update unless the app also wants the 327 // mode on. this will update the origin of the mode and let the owner turn it 328 // off when the context ends 329 if (r.condition != null && r.condition.source == UPDATE_ORIGIN_USER) { 330 if (r.condition.state == STATE_TRUE && c.state == STATE_TRUE) { 331 r.condition = c; 332 } 333 } else { 334 r.condition = c; 335 } 336 } else { 337 r.condition = c; 338 } 339 } 340 } 341 final int N = conditions.length; 342 for (int i = 0; i < N; i++) { 343 final Condition c = conditions[i]; 344 if (mCallback != null) { 345 mCallback.onConditionChanged(c.id, c); 346 } 347 } 348 } 349 findConditionProvider(ComponentName component)350 public IConditionProvider findConditionProvider(ComponentName component) { 351 if (component == null) return null; 352 for (ManagedServiceInfo service : getServices()) { 353 if (component.equals(service.component)) { 354 return provider(service); 355 } 356 } 357 return null; 358 } 359 findCondition(ComponentName component, Uri conditionId)360 public Condition findCondition(ComponentName component, Uri conditionId) { 361 if (component == null || conditionId == null) return null; 362 synchronized (mMutex) { 363 final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/); 364 return r != null ? r.condition : null; 365 } 366 } 367 ensureRecordExists(ComponentName component, Uri conditionId, IConditionProvider provider)368 public void ensureRecordExists(ComponentName component, Uri conditionId, 369 IConditionProvider provider) { 370 synchronized (mMutex) { 371 // constructed by convention, make sure the record exists... 372 final ConditionRecord r = getRecordLocked(conditionId, component, true /*create*/); 373 if (r.info == null) { 374 // ... and is associated with the in-process service 375 r.info = checkServiceTokenLocked(provider); 376 } 377 } 378 } 379 subscribeIfNecessary(ComponentName component, Uri conditionId)380 public boolean subscribeIfNecessary(ComponentName component, Uri conditionId) { 381 synchronized (mMutex) { 382 final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/); 383 if (r == null) { 384 Slog.w(TAG, "Unable to subscribe to " + component + " " + conditionId); 385 return false; 386 } 387 if (r.subscribed) return true; 388 subscribeLocked(r); 389 return r.subscribed; 390 } 391 } 392 unsubscribeIfNecessary(ComponentName component, Uri conditionId)393 public void unsubscribeIfNecessary(ComponentName component, Uri conditionId) { 394 synchronized (mMutex) { 395 final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/); 396 if (r == null) { 397 Slog.w(TAG, "Unable to unsubscribe to " + component + " " + conditionId); 398 return; 399 } 400 if (!r.subscribed) return; 401 unsubscribeLocked(r);; 402 } 403 } 404 subscribeLocked(ConditionRecord r)405 private void subscribeLocked(ConditionRecord r) { 406 if (DEBUG) Slog.d(TAG, "subscribeLocked " + r); 407 final IConditionProvider provider = provider(r); 408 RemoteException re = null; 409 if (provider != null) { 410 try { 411 Slog.d(TAG, "Subscribing to " + r.id + " with " + r.component); 412 provider.onSubscribe(r.id); 413 r.subscribed = true; 414 } catch (RemoteException e) { 415 Slog.w(TAG, "Error subscribing to " + r, e); 416 re = e; 417 } 418 } 419 ZenLog.traceSubscribe(r != null ? r.id : null, provider, re); 420 } 421 422 @SafeVarargs safeSet(T... items)423 private static <T> ArraySet<T> safeSet(T... items) { 424 final ArraySet<T> rt = new ArraySet<T>(); 425 if (items == null || items.length == 0) return rt; 426 final int N = items.length; 427 for (int i = 0; i < N; i++) { 428 final T item = items[i]; 429 if (item != null) { 430 rt.add(item); 431 } 432 } 433 return rt; 434 } 435 unsubscribeLocked(ConditionRecord r)436 private void unsubscribeLocked(ConditionRecord r) { 437 if (DEBUG) Slog.d(TAG, "unsubscribeLocked " + r); 438 final IConditionProvider provider = provider(r); 439 RemoteException re = null; 440 if (provider != null) { 441 try { 442 provider.onUnsubscribe(r.id); 443 } catch (RemoteException e) { 444 Slog.w(TAG, "Error unsubscribing to " + r, e); 445 re = e; 446 } 447 r.subscribed = false; 448 } 449 ZenLog.traceUnsubscribe(r != null ? r.id : null, provider, re); 450 } 451 provider(ConditionRecord r)452 private static IConditionProvider provider(ConditionRecord r) { 453 return r == null ? null : provider(r.info); 454 } 455 provider(ManagedServiceInfo info)456 private static IConditionProvider provider(ManagedServiceInfo info) { 457 return info == null ? null : (IConditionProvider) info.service; 458 } 459 resetDefaultFromConfig()460 void resetDefaultFromConfig() { 461 synchronized (mDefaultsLock) { 462 mDefaultComponents.clear(); 463 mDefaultPackages.clear(); 464 } 465 loadDefaultsFromConfig(); 466 } 467 removeDefaultFromConfig(int userId)468 boolean removeDefaultFromConfig(int userId) { 469 boolean removed = false; 470 String defaultDndDenied = mContext.getResources().getString( 471 R.string.config_defaultDndDeniedPackages); 472 if (defaultDndDenied != null) { 473 String[] dnds = defaultDndDenied.split(ManagedServices.ENABLED_SERVICES_SEPARATOR); 474 for (int i = 0; i < dnds.length; i++) { 475 if (TextUtils.isEmpty(dnds[i])) { 476 continue; 477 } 478 removed |= removePackageFromApprovedLists(userId, dnds[i], "remove from config"); 479 } 480 } 481 return removed; 482 } 483 removePackageFromApprovedLists(int userId, String pkg, String reason)484 private boolean removePackageFromApprovedLists(int userId, String pkg, String reason) { 485 boolean removed = false; 486 synchronized (mApproved) { 487 final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get( 488 userId); 489 if (approvedByType != null) { 490 int approvedByTypeSize = approvedByType.size(); 491 for (int i = 0; i < approvedByTypeSize; i++) { 492 final ArraySet<String> approved = approvedByType.valueAt(i); 493 int approvedSize = approved.size(); 494 for (int j = approvedSize - 1; j >= 0; j--) { 495 final String packageOrComponent = approved.valueAt(j); 496 final String packageName = getPackageName(packageOrComponent); 497 if (TextUtils.equals(pkg, packageName)) { 498 approved.removeAt(j); 499 removed = true; 500 if (DEBUG) { 501 Slog.v(TAG, "Removing " + packageOrComponent 502 + " from approved list; " + reason); 503 } 504 } 505 } 506 } 507 } 508 } 509 return removed; 510 } 511 512 private static class ConditionRecord { 513 public final Uri id; 514 public final ComponentName component; 515 public Condition condition; 516 public ManagedServiceInfo info; 517 public boolean subscribed; 518 ConditionRecord(Uri id, ComponentName component)519 private ConditionRecord(Uri id, ComponentName component) { 520 this.id = id; 521 this.component = component; 522 } 523 524 @Override toString()525 public String toString() { 526 final StringBuilder sb = new StringBuilder("ConditionRecord[id=") 527 .append(id).append(",component=").append(component) 528 .append(",subscribed=").append(subscribed); 529 return sb.append(']').toString(); 530 } 531 } 532 533 public interface Callback { onBootComplete()534 void onBootComplete(); onServiceAdded(ComponentName component)535 void onServiceAdded(ComponentName component); onConditionChanged(Uri id, Condition condition)536 void onConditionChanged(Uri id, Condition condition); onUserSwitched()537 void onUserSwitched(); 538 } 539 540 } 541