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 import android.util.TypedXmlSerializer; 40 41 import com.android.internal.R; 42 import com.android.internal.annotations.VisibleForTesting; 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 getRequiredPermission()254 protected String getRequiredPermission() { 255 return null; 256 } 257 checkServiceToken(IConditionProvider provider)258 public ManagedServiceInfo checkServiceToken(IConditionProvider provider) { 259 synchronized(mMutex) { 260 return checkServiceTokenLocked(provider); 261 } 262 } 263 removeDuplicateConditions(String pkg, Condition[] conditions)264 private Condition[] removeDuplicateConditions(String pkg, Condition[] conditions) { 265 if (conditions == null || conditions.length == 0) return null; 266 final int N = conditions.length; 267 final ArrayMap<Uri, Condition> valid = new ArrayMap<Uri, Condition>(N); 268 for (int i = 0; i < N; i++) { 269 final Uri id = conditions[i].id; 270 if (valid.containsKey(id)) { 271 Slog.w(TAG, "Ignoring condition from " + pkg + " for duplicate id: " + id); 272 continue; 273 } 274 valid.put(id, conditions[i]); 275 } 276 if (valid.size() == 0) return null; 277 if (valid.size() == N) return conditions; 278 final Condition[] rt = new Condition[valid.size()]; 279 for (int i = 0; i < rt.length; i++) { 280 rt[i] = valid.valueAt(i); 281 } 282 return rt; 283 } 284 getRecordLocked(Uri id, ComponentName component, boolean create)285 private ConditionRecord getRecordLocked(Uri id, ComponentName component, boolean create) { 286 if (id == null || component == null) return null; 287 final int N = mRecords.size(); 288 for (int i = 0; i < N; i++) { 289 final ConditionRecord r = mRecords.get(i); 290 if (r.id.equals(id) && r.component.equals(component)) { 291 return r; 292 } 293 } 294 if (create) { 295 final ConditionRecord r = new ConditionRecord(id, component); 296 mRecords.add(r); 297 return r; 298 } 299 return null; 300 } 301 notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions)302 public void notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions) { 303 synchronized(mMutex) { 304 if (DEBUG) Slog.d(TAG, "notifyConditions pkg=" + pkg + " info=" + info + " conditions=" 305 + (conditions == null ? null : Arrays.asList(conditions))); 306 conditions = removeDuplicateConditions(pkg, conditions); 307 if (conditions == null || conditions.length == 0) return; 308 final int N = conditions.length; 309 for (int i = 0; i < N; i++) { 310 final Condition c = conditions[i]; 311 final ConditionRecord r = getRecordLocked(c.id, info.component, true /*create*/); 312 r.info = info; 313 r.condition = c; 314 } 315 } 316 final int N = conditions.length; 317 for (int i = 0; i < N; i++) { 318 final Condition c = conditions[i]; 319 if (mCallback != null) { 320 mCallback.onConditionChanged(c.id, c); 321 } 322 } 323 } 324 findConditionProvider(ComponentName component)325 public IConditionProvider findConditionProvider(ComponentName component) { 326 if (component == null) return null; 327 for (ManagedServiceInfo service : getServices()) { 328 if (component.equals(service.component)) { 329 return provider(service); 330 } 331 } 332 return null; 333 } 334 findCondition(ComponentName component, Uri conditionId)335 public Condition findCondition(ComponentName component, Uri conditionId) { 336 if (component == null || conditionId == null) return null; 337 synchronized (mMutex) { 338 final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/); 339 return r != null ? r.condition : null; 340 } 341 } 342 ensureRecordExists(ComponentName component, Uri conditionId, IConditionProvider provider)343 public void ensureRecordExists(ComponentName component, Uri conditionId, 344 IConditionProvider provider) { 345 synchronized (mMutex) { 346 // constructed by convention, make sure the record exists... 347 final ConditionRecord r = getRecordLocked(conditionId, component, true /*create*/); 348 if (r.info == null) { 349 // ... and is associated with the in-process service 350 r.info = checkServiceTokenLocked(provider); 351 } 352 } 353 } 354 subscribeIfNecessary(ComponentName component, Uri conditionId)355 public boolean subscribeIfNecessary(ComponentName component, Uri conditionId) { 356 synchronized (mMutex) { 357 final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/); 358 if (r == null) { 359 Slog.w(TAG, "Unable to subscribe to " + component + " " + conditionId); 360 return false; 361 } 362 if (r.subscribed) return true; 363 subscribeLocked(r); 364 return r.subscribed; 365 } 366 } 367 unsubscribeIfNecessary(ComponentName component, Uri conditionId)368 public void unsubscribeIfNecessary(ComponentName component, Uri conditionId) { 369 synchronized (mMutex) { 370 final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/); 371 if (r == null) { 372 Slog.w(TAG, "Unable to unsubscribe to " + component + " " + conditionId); 373 return; 374 } 375 if (!r.subscribed) return; 376 unsubscribeLocked(r);; 377 } 378 } 379 subscribeLocked(ConditionRecord r)380 private void subscribeLocked(ConditionRecord r) { 381 if (DEBUG) Slog.d(TAG, "subscribeLocked " + r); 382 final IConditionProvider provider = provider(r); 383 RemoteException re = null; 384 if (provider != null) { 385 try { 386 Slog.d(TAG, "Subscribing to " + r.id + " with " + r.component); 387 provider.onSubscribe(r.id); 388 r.subscribed = true; 389 } catch (RemoteException e) { 390 Slog.w(TAG, "Error subscribing to " + r, e); 391 re = e; 392 } 393 } 394 ZenLog.traceSubscribe(r != null ? r.id : null, provider, re); 395 } 396 397 @SafeVarargs safeSet(T... items)398 private static <T> ArraySet<T> safeSet(T... items) { 399 final ArraySet<T> rt = new ArraySet<T>(); 400 if (items == null || items.length == 0) return rt; 401 final int N = items.length; 402 for (int i = 0; i < N; i++) { 403 final T item = items[i]; 404 if (item != null) { 405 rt.add(item); 406 } 407 } 408 return rt; 409 } 410 unsubscribeLocked(ConditionRecord r)411 private void unsubscribeLocked(ConditionRecord r) { 412 if (DEBUG) Slog.d(TAG, "unsubscribeLocked " + r); 413 final IConditionProvider provider = provider(r); 414 RemoteException re = null; 415 if (provider != null) { 416 try { 417 provider.onUnsubscribe(r.id); 418 } catch (RemoteException e) { 419 Slog.w(TAG, "Error unsubscribing to " + r, e); 420 re = e; 421 } 422 r.subscribed = false; 423 } 424 ZenLog.traceUnsubscribe(r != null ? r.id : null, provider, re); 425 } 426 provider(ConditionRecord r)427 private static IConditionProvider provider(ConditionRecord r) { 428 return r == null ? null : provider(r.info); 429 } 430 provider(ManagedServiceInfo info)431 private static IConditionProvider provider(ManagedServiceInfo info) { 432 return info == null ? null : (IConditionProvider) info.service; 433 } 434 435 private static class ConditionRecord { 436 public final Uri id; 437 public final ComponentName component; 438 public Condition condition; 439 public ManagedServiceInfo info; 440 public boolean subscribed; 441 ConditionRecord(Uri id, ComponentName component)442 private ConditionRecord(Uri id, ComponentName component) { 443 this.id = id; 444 this.component = component; 445 } 446 447 @Override toString()448 public String toString() { 449 final StringBuilder sb = new StringBuilder("ConditionRecord[id=") 450 .append(id).append(",component=").append(component) 451 .append(",subscribed=").append(subscribed); 452 return sb.append(']').toString(); 453 } 454 } 455 456 public interface Callback { onBootComplete()457 void onBootComplete(); onServiceAdded(ComponentName component)458 void onServiceAdded(ComponentName component); onConditionChanged(Uri id, Condition condition)459 void onConditionChanged(Uri id, Condition condition); onUserSwitched()460 void onUserSwitched(); 461 } 462 463 } 464