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