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 if (mCallback != null) { 173 mCallback.onBootComplete(); 174 } 175 } 176 177 @Override onUserSwitched(int user)178 public void onUserSwitched(int user) { 179 super.onUserSwitched(user); 180 if (mCallback != null) { 181 mCallback.onUserSwitched(); 182 } 183 } 184 185 @Override onServiceAdded(ManagedServiceInfo info)186 protected void onServiceAdded(ManagedServiceInfo info) { 187 final IConditionProvider provider = provider(info); 188 try { 189 provider.onConnected(); 190 } catch (RemoteException e) { 191 Slog.e(TAG, "can't connect to service " + info, e); 192 // we tried 193 } 194 if (mCallback != null) { 195 mCallback.onServiceAdded(info.component); 196 } 197 } 198 199 @Override ensureFilters(ServiceInfo si, int userId)200 protected void ensureFilters(ServiceInfo si, int userId) { 201 // nothing to filter 202 } 203 204 @Override loadDefaultsFromConfig()205 protected void loadDefaultsFromConfig() { 206 String defaultDndAccess = mContext.getResources().getString( 207 R.string.config_defaultDndAccessPackages); 208 if (defaultDndAccess != null) { 209 String[] dnds = defaultDndAccess.split(ManagedServices.ENABLED_SERVICES_SEPARATOR); 210 for (int i = 0; i < dnds.length; i++) { 211 if (TextUtils.isEmpty(dnds[i])) { 212 continue; 213 } 214 addDefaultComponentOrPackage(dnds[i]); 215 } 216 } 217 } 218 219 @Override onServiceRemovedLocked(ManagedServiceInfo removed)220 protected void onServiceRemovedLocked(ManagedServiceInfo removed) { 221 if (removed == null) return; 222 for (int i = mRecords.size() - 1; i >= 0; i--) { 223 final ConditionRecord r = mRecords.get(i); 224 if (!r.component.equals(removed.component)) continue; 225 mRecords.remove(i); 226 } 227 } 228 229 @Override onPackagesChanged(boolean removingPackage, String[] pkgList, int[] uid)230 public void onPackagesChanged(boolean removingPackage, String[] pkgList, int[] uid) { 231 if (removingPackage) { 232 INotificationManager inm = NotificationManager.getService(); 233 234 if (pkgList != null && (pkgList.length > 0)) { 235 for (String pkgName : pkgList) { 236 try { 237 inm.removeAutomaticZenRules(pkgName); 238 inm.setNotificationPolicyAccessGranted(pkgName, false); 239 } catch (Exception e) { 240 Slog.e(TAG, "Failed to clean up rules for " + pkgName, e); 241 } 242 } 243 } 244 } 245 super.onPackagesChanged(removingPackage, pkgList, uid); 246 } 247 248 @Override isValidEntry(String packageOrComponent, int userId)249 protected boolean isValidEntry(String packageOrComponent, int userId) { 250 return true; 251 } 252 253 @Override allowRebindForParentUser()254 protected boolean allowRebindForParentUser() { 255 return true; 256 } 257 258 @Override getRequiredPermission()259 protected String getRequiredPermission() { 260 return null; 261 } 262 checkServiceToken(IConditionProvider provider)263 public ManagedServiceInfo checkServiceToken(IConditionProvider provider) { 264 synchronized(mMutex) { 265 return checkServiceTokenLocked(provider); 266 } 267 } 268 getValidConditions(String pkg, Condition[] conditions)269 private Condition[] getValidConditions(String pkg, Condition[] conditions) { 270 if (conditions == null || conditions.length == 0) return null; 271 final int N = conditions.length; 272 final ArrayMap<Uri, Condition> valid = new ArrayMap<Uri, Condition>(N); 273 for (int i = 0; i < N; i++) { 274 if (conditions[i] == null) { 275 Slog.w(TAG, "Ignoring null condition from " + pkg); 276 continue; 277 } 278 final Uri id = conditions[i].id; 279 if (valid.containsKey(id)) { 280 Slog.w(TAG, "Ignoring condition from " + pkg + " for duplicate id: " + id); 281 continue; 282 } 283 valid.put(id, conditions[i]); 284 } 285 if (valid.size() == 0) return null; 286 if (valid.size() == N) return conditions; 287 final Condition[] rt = new Condition[valid.size()]; 288 for (int i = 0; i < rt.length; i++) { 289 rt[i] = valid.valueAt(i); 290 } 291 return rt; 292 } 293 getRecordLocked(Uri id, ComponentName component, boolean create)294 private ConditionRecord getRecordLocked(Uri id, ComponentName component, boolean create) { 295 if (id == null || component == null) return null; 296 final int N = mRecords.size(); 297 for (int i = 0; i < N; i++) { 298 final ConditionRecord r = mRecords.get(i); 299 if (r.id.equals(id) && r.component.equals(component)) { 300 return r; 301 } 302 } 303 if (create) { 304 final ConditionRecord r = new ConditionRecord(id, component); 305 mRecords.add(r); 306 return r; 307 } 308 return null; 309 } 310 notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions)311 public void notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions) { 312 synchronized(mMutex) { 313 if (DEBUG) Slog.d(TAG, "notifyConditions pkg=" + pkg + " info=" + info + " conditions=" 314 + (conditions == null ? null : Arrays.asList(conditions))); 315 conditions = getValidConditions(pkg, conditions); 316 if (conditions == null || conditions.length == 0) return; 317 final int N = conditions.length; 318 for (int i = 0; i < N; i++) { 319 final Condition c = conditions[i]; 320 final ConditionRecord r = getRecordLocked(c.id, info.component, true /*create*/); 321 r.info = info; 322 r.condition = c; 323 } 324 } 325 final int N = conditions.length; 326 for (int i = 0; i < N; i++) { 327 final Condition c = conditions[i]; 328 if (mCallback != null) { 329 mCallback.onConditionChanged(c.id, c); 330 } 331 } 332 } 333 findConditionProvider(ComponentName component)334 public IConditionProvider findConditionProvider(ComponentName component) { 335 if (component == null) return null; 336 for (ManagedServiceInfo service : getServices()) { 337 if (component.equals(service.component)) { 338 return provider(service); 339 } 340 } 341 return null; 342 } 343 findCondition(ComponentName component, Uri conditionId)344 public Condition findCondition(ComponentName component, Uri conditionId) { 345 if (component == null || conditionId == null) return null; 346 synchronized (mMutex) { 347 final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/); 348 return r != null ? r.condition : null; 349 } 350 } 351 ensureRecordExists(ComponentName component, Uri conditionId, IConditionProvider provider)352 public void ensureRecordExists(ComponentName component, Uri conditionId, 353 IConditionProvider provider) { 354 synchronized (mMutex) { 355 // constructed by convention, make sure the record exists... 356 final ConditionRecord r = getRecordLocked(conditionId, component, true /*create*/); 357 if (r.info == null) { 358 // ... and is associated with the in-process service 359 r.info = checkServiceTokenLocked(provider); 360 } 361 } 362 } 363 subscribeIfNecessary(ComponentName component, Uri conditionId)364 public boolean subscribeIfNecessary(ComponentName component, Uri conditionId) { 365 synchronized (mMutex) { 366 final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/); 367 if (r == null) { 368 Slog.w(TAG, "Unable to subscribe to " + component + " " + conditionId); 369 return false; 370 } 371 if (r.subscribed) return true; 372 subscribeLocked(r); 373 return r.subscribed; 374 } 375 } 376 unsubscribeIfNecessary(ComponentName component, Uri conditionId)377 public void unsubscribeIfNecessary(ComponentName component, Uri conditionId) { 378 synchronized (mMutex) { 379 final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/); 380 if (r == null) { 381 Slog.w(TAG, "Unable to unsubscribe to " + component + " " + conditionId); 382 return; 383 } 384 if (!r.subscribed) return; 385 unsubscribeLocked(r);; 386 } 387 } 388 subscribeLocked(ConditionRecord r)389 private void subscribeLocked(ConditionRecord r) { 390 if (DEBUG) Slog.d(TAG, "subscribeLocked " + r); 391 final IConditionProvider provider = provider(r); 392 RemoteException re = null; 393 if (provider != null) { 394 try { 395 Slog.d(TAG, "Subscribing to " + r.id + " with " + r.component); 396 provider.onSubscribe(r.id); 397 r.subscribed = true; 398 } catch (RemoteException e) { 399 Slog.w(TAG, "Error subscribing to " + r, e); 400 re = e; 401 } 402 } 403 ZenLog.traceSubscribe(r != null ? r.id : null, provider, re); 404 } 405 406 @SafeVarargs safeSet(T... items)407 private static <T> ArraySet<T> safeSet(T... items) { 408 final ArraySet<T> rt = new ArraySet<T>(); 409 if (items == null || items.length == 0) return rt; 410 final int N = items.length; 411 for (int i = 0; i < N; i++) { 412 final T item = items[i]; 413 if (item != null) { 414 rt.add(item); 415 } 416 } 417 return rt; 418 } 419 unsubscribeLocked(ConditionRecord r)420 private void unsubscribeLocked(ConditionRecord r) { 421 if (DEBUG) Slog.d(TAG, "unsubscribeLocked " + r); 422 final IConditionProvider provider = provider(r); 423 RemoteException re = null; 424 if (provider != null) { 425 try { 426 provider.onUnsubscribe(r.id); 427 } catch (RemoteException e) { 428 Slog.w(TAG, "Error unsubscribing to " + r, e); 429 re = e; 430 } 431 r.subscribed = false; 432 } 433 ZenLog.traceUnsubscribe(r != null ? r.id : null, provider, re); 434 } 435 provider(ConditionRecord r)436 private static IConditionProvider provider(ConditionRecord r) { 437 return r == null ? null : provider(r.info); 438 } 439 provider(ManagedServiceInfo info)440 private static IConditionProvider provider(ManagedServiceInfo info) { 441 return info == null ? null : (IConditionProvider) info.service; 442 } 443 444 private static class ConditionRecord { 445 public final Uri id; 446 public final ComponentName component; 447 public Condition condition; 448 public ManagedServiceInfo info; 449 public boolean subscribed; 450 ConditionRecord(Uri id, ComponentName component)451 private ConditionRecord(Uri id, ComponentName component) { 452 this.id = id; 453 this.component = component; 454 } 455 456 @Override toString()457 public String toString() { 458 final StringBuilder sb = new StringBuilder("ConditionRecord[id=") 459 .append(id).append(",component=").append(component) 460 .append(",subscribed=").append(subscribed); 461 return sb.append(']').toString(); 462 } 463 } 464 465 public interface Callback { onBootComplete()466 void onBootComplete(); onServiceAdded(ComponentName component)467 void onServiceAdded(ComponentName component); onConditionChanged(Uri id, Condition condition)468 void onConditionChanged(Uri id, Condition condition); onUserSwitched()469 void onUserSwitched(); 470 } 471 472 } 473