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