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.annotation.NonNull; 20 import android.app.INotificationManager; 21 import android.app.NotificationManager; 22 import android.content.ComponentName; 23 import android.content.ContentResolver; 24 import android.content.Context; 25 import android.net.Uri; 26 import android.os.Handler; 27 import android.os.IBinder; 28 import android.os.IInterface; 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.server.notification.NotificationManagerService.DumpFilter; 42 43 import java.io.PrintWriter; 44 import java.util.ArrayList; 45 import java.util.Arrays; 46 47 public class ConditionProviders extends ManagedServices { 48 private final ArrayList<ConditionRecord> mRecords = new ArrayList<>(); 49 private final ArraySet<String> mSystemConditionProviderNames; 50 private final ArraySet<SystemConditionProviderService> mSystemConditionProviders 51 = new ArraySet<>(); 52 53 private Callback mCallback; 54 ConditionProviders(Context context, Handler handler, UserProfiles userProfiles)55 public ConditionProviders(Context context, Handler handler, UserProfiles userProfiles) { 56 super(context, handler, new Object(), userProfiles); 57 mSystemConditionProviderNames = safeSet(PropConfig.getStringArray(mContext, 58 "system.condition.providers", 59 R.array.config_system_condition_providers)); 60 } 61 setCallback(Callback callback)62 public void setCallback(Callback callback) { 63 mCallback = callback; 64 } 65 isSystemProviderEnabled(String path)66 public boolean isSystemProviderEnabled(String path) { 67 return mSystemConditionProviderNames.contains(path); 68 } 69 addSystemProvider(SystemConditionProviderService service)70 public void addSystemProvider(SystemConditionProviderService service) { 71 mSystemConditionProviders.add(service); 72 service.attachBase(mContext); 73 registerService(service.asInterface(), service.getComponent(), UserHandle.USER_SYSTEM); 74 } 75 getSystemProviders()76 public Iterable<SystemConditionProviderService> getSystemProviders() { 77 return mSystemConditionProviders; 78 } 79 80 @Override getConfig()81 protected Config getConfig() { 82 final Config c = new Config(); 83 c.caption = "condition provider"; 84 c.serviceInterface = ConditionProviderService.SERVICE_INTERFACE; 85 c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES; 86 c.secondarySettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS; 87 c.bindPermission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE; 88 c.settingsAction = Settings.ACTION_CONDITION_PROVIDER_SETTINGS; 89 c.clientLabel = R.string.condition_provider_service_binding_label; 90 return c; 91 } 92 93 @Override dump(PrintWriter pw, DumpFilter filter)94 public void dump(PrintWriter pw, DumpFilter filter) { 95 super.dump(pw, filter); 96 synchronized(mMutex) { 97 pw.print(" mRecords("); pw.print(mRecords.size()); pw.println("):"); 98 for (int i = 0; i < mRecords.size(); i++) { 99 final ConditionRecord r = mRecords.get(i); 100 if (filter != null && !filter.matches(r.component)) continue; 101 pw.print(" "); pw.println(r); 102 final String countdownDesc = CountdownConditionProvider.tryParseDescription(r.id); 103 if (countdownDesc != null) { 104 pw.print(" ("); pw.print(countdownDesc); pw.println(")"); 105 } 106 } 107 } 108 pw.print(" mSystemConditionProviders: "); pw.println(mSystemConditionProviderNames); 109 for (int i = 0; i < mSystemConditionProviders.size(); i++) { 110 mSystemConditionProviders.valueAt(i).dump(pw, filter); 111 } 112 } 113 114 @Override asInterface(IBinder binder)115 protected IInterface asInterface(IBinder binder) { 116 return IConditionProvider.Stub.asInterface(binder); 117 } 118 119 @Override checkType(IInterface service)120 protected boolean checkType(IInterface service) { 121 return service instanceof IConditionProvider; 122 } 123 124 @Override onBootPhaseAppsCanStart()125 public void onBootPhaseAppsCanStart() { 126 super.onBootPhaseAppsCanStart(); 127 for (int i = 0; i < mSystemConditionProviders.size(); i++) { 128 mSystemConditionProviders.valueAt(i).onBootComplete(); 129 } 130 if (mCallback != null) { 131 mCallback.onBootComplete(); 132 } 133 } 134 135 @Override onUserSwitched(int user)136 public void onUserSwitched(int user) { 137 super.onUserSwitched(user); 138 if (mCallback != null) { 139 mCallback.onUserSwitched(); 140 } 141 } 142 143 @Override onServiceAdded(ManagedServiceInfo info)144 protected void onServiceAdded(ManagedServiceInfo info) { 145 final IConditionProvider provider = provider(info); 146 try { 147 provider.onConnected(); 148 } catch (RemoteException e) { 149 // we tried 150 } 151 if (mCallback != null) { 152 mCallback.onServiceAdded(info.component); 153 } 154 } 155 156 @Override onServiceRemovedLocked(ManagedServiceInfo removed)157 protected void onServiceRemovedLocked(ManagedServiceInfo removed) { 158 if (removed == null) return; 159 for (int i = mRecords.size() - 1; i >= 0; i--) { 160 final ConditionRecord r = mRecords.get(i); 161 if (!r.component.equals(removed.component)) continue; 162 mRecords.remove(i); 163 } 164 } 165 166 @Override onPackagesChanged(boolean removingPackage, String[] pkgList)167 public void onPackagesChanged(boolean removingPackage, String[] pkgList) { 168 if (removingPackage) { 169 INotificationManager inm = NotificationManager.getService(); 170 171 if (pkgList != null && (pkgList.length > 0)) { 172 for (String pkgName : pkgList) { 173 try { 174 inm.removeAutomaticZenRules(pkgName); 175 inm.setNotificationPolicyAccessGranted(pkgName, false); 176 } catch (Exception e) { 177 Slog.e(TAG, "Failed to clean up rules for " + pkgName, e); 178 } 179 } 180 } 181 } 182 super.onPackagesChanged(removingPackage, pkgList); 183 } 184 checkServiceToken(IConditionProvider provider)185 public ManagedServiceInfo checkServiceToken(IConditionProvider provider) { 186 synchronized(mMutex) { 187 return checkServiceTokenLocked(provider); 188 } 189 } 190 removeDuplicateConditions(String pkg, Condition[] conditions)191 private Condition[] removeDuplicateConditions(String pkg, Condition[] conditions) { 192 if (conditions == null || conditions.length == 0) return null; 193 final int N = conditions.length; 194 final ArrayMap<Uri, Condition> valid = new ArrayMap<Uri, Condition>(N); 195 for (int i = 0; i < N; i++) { 196 final Uri id = conditions[i].id; 197 if (valid.containsKey(id)) { 198 Slog.w(TAG, "Ignoring condition from " + pkg + " for duplicate id: " + id); 199 continue; 200 } 201 valid.put(id, conditions[i]); 202 } 203 if (valid.size() == 0) return null; 204 if (valid.size() == N) return conditions; 205 final Condition[] rt = new Condition[valid.size()]; 206 for (int i = 0; i < rt.length; i++) { 207 rt[i] = valid.valueAt(i); 208 } 209 return rt; 210 } 211 getRecordLocked(Uri id, ComponentName component, boolean create)212 private ConditionRecord getRecordLocked(Uri id, ComponentName component, boolean create) { 213 if (id == null || component == null) return null; 214 final int N = mRecords.size(); 215 for (int i = 0; i < N; i++) { 216 final ConditionRecord r = mRecords.get(i); 217 if (r.id.equals(id) && r.component.equals(component)) { 218 return r; 219 } 220 } 221 if (create) { 222 final ConditionRecord r = new ConditionRecord(id, component); 223 mRecords.add(r); 224 return r; 225 } 226 return null; 227 } 228 notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions)229 public void notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions) { 230 synchronized(mMutex) { 231 if (DEBUG) Slog.d(TAG, "notifyConditions pkg=" + pkg + " info=" + info + " conditions=" 232 + (conditions == null ? null : Arrays.asList(conditions))); 233 conditions = removeDuplicateConditions(pkg, conditions); 234 if (conditions == null || conditions.length == 0) return; 235 final int N = conditions.length; 236 for (int i = 0; i < N; i++) { 237 final Condition c = conditions[i]; 238 final ConditionRecord r = getRecordLocked(c.id, info.component, true /*create*/); 239 r.info = info; 240 r.condition = c; 241 } 242 } 243 final int N = conditions.length; 244 for (int i = 0; i < N; i++) { 245 final Condition c = conditions[i]; 246 if (mCallback != null) { 247 mCallback.onConditionChanged(c.id, c); 248 } 249 } 250 } 251 findConditionProvider(ComponentName component)252 public IConditionProvider findConditionProvider(ComponentName component) { 253 if (component == null) return null; 254 for (ManagedServiceInfo service : mServices) { 255 if (component.equals(service.component)) { 256 return provider(service); 257 } 258 } 259 return null; 260 } 261 findCondition(ComponentName component, Uri conditionId)262 public Condition findCondition(ComponentName component, Uri conditionId) { 263 if (component == null || conditionId == null) return null; 264 synchronized (mMutex) { 265 final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/); 266 return r != null ? r.condition : null; 267 } 268 } 269 ensureRecordExists(ComponentName component, Uri conditionId, IConditionProvider provider)270 public void ensureRecordExists(ComponentName component, Uri conditionId, 271 IConditionProvider provider) { 272 // constructed by convention, make sure the record exists... 273 final ConditionRecord r = getRecordLocked(conditionId, component, true /*create*/); 274 if (r.info == null) { 275 // ... and is associated with the in-process service 276 r.info = checkServiceTokenLocked(provider); 277 } 278 } 279 280 @Override loadComponentNamesFromSetting(String settingName, int userId)281 protected @NonNull ArraySet<ComponentName> loadComponentNamesFromSetting(String settingName, 282 int userId) { 283 final ContentResolver cr = mContext.getContentResolver(); 284 String settingValue = Settings.Secure.getStringForUser( 285 cr, 286 settingName, 287 userId); 288 if (TextUtils.isEmpty(settingValue)) 289 return new ArraySet<>(); 290 String[] packages = settingValue.split(ENABLED_SERVICES_SEPARATOR); 291 ArraySet<ComponentName> result = new ArraySet<>(packages.length); 292 for (int i = 0; i < packages.length; i++) { 293 if (!TextUtils.isEmpty(packages[i])) { 294 final ComponentName component = ComponentName.unflattenFromString(packages[i]); 295 if (component != null) { 296 result.addAll(queryPackageForServices(component.getPackageName(), userId)); 297 } else { 298 result.addAll(queryPackageForServices(packages[i], userId)); 299 } 300 } 301 } 302 return result; 303 } 304 subscribeIfNecessary(ComponentName component, Uri conditionId)305 public boolean subscribeIfNecessary(ComponentName component, Uri conditionId) { 306 synchronized (mMutex) { 307 final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/); 308 if (r == null) { 309 Slog.w(TAG, "Unable to subscribe to " + component + " " + conditionId); 310 return false; 311 } 312 if (r.subscribed) return true; 313 subscribeLocked(r); 314 return r.subscribed; 315 } 316 } 317 unsubscribeIfNecessary(ComponentName component, Uri conditionId)318 public void unsubscribeIfNecessary(ComponentName component, Uri conditionId) { 319 synchronized (mMutex) { 320 final ConditionRecord r = getRecordLocked(conditionId, component, false /*create*/); 321 if (r == null) { 322 Slog.w(TAG, "Unable to unsubscribe to " + component + " " + conditionId); 323 return; 324 } 325 if (!r.subscribed) return; 326 unsubscribeLocked(r);; 327 } 328 } 329 subscribeLocked(ConditionRecord r)330 private void subscribeLocked(ConditionRecord r) { 331 if (DEBUG) Slog.d(TAG, "subscribeLocked " + r); 332 final IConditionProvider provider = provider(r); 333 RemoteException re = null; 334 if (provider != null) { 335 try { 336 Slog.d(TAG, "Subscribing to " + r.id + " with " + r.component); 337 provider.onSubscribe(r.id); 338 r.subscribed = true; 339 } catch (RemoteException e) { 340 Slog.w(TAG, "Error subscribing to " + r, e); 341 re = e; 342 } 343 } 344 ZenLog.traceSubscribe(r != null ? r.id : null, provider, re); 345 } 346 347 @SafeVarargs safeSet(T... items)348 private static <T> ArraySet<T> safeSet(T... items) { 349 final ArraySet<T> rt = new ArraySet<T>(); 350 if (items == null || items.length == 0) return rt; 351 final int N = items.length; 352 for (int i = 0; i < N; i++) { 353 final T item = items[i]; 354 if (item != null) { 355 rt.add(item); 356 } 357 } 358 return rt; 359 } 360 unsubscribeLocked(ConditionRecord r)361 private void unsubscribeLocked(ConditionRecord r) { 362 if (DEBUG) Slog.d(TAG, "unsubscribeLocked " + r); 363 final IConditionProvider provider = provider(r); 364 RemoteException re = null; 365 if (provider != null) { 366 try { 367 provider.onUnsubscribe(r.id); 368 } catch (RemoteException e) { 369 Slog.w(TAG, "Error unsubscribing to " + r, e); 370 re = e; 371 } 372 r.subscribed = false; 373 } 374 ZenLog.traceUnsubscribe(r != null ? r.id : null, provider, re); 375 } 376 provider(ConditionRecord r)377 private static IConditionProvider provider(ConditionRecord r) { 378 return r == null ? null : provider(r.info); 379 } 380 provider(ManagedServiceInfo info)381 private static IConditionProvider provider(ManagedServiceInfo info) { 382 return info == null ? null : (IConditionProvider) info.service; 383 } 384 385 private static class ConditionRecord { 386 public final Uri id; 387 public final ComponentName component; 388 public Condition condition; 389 public ManagedServiceInfo info; 390 public boolean subscribed; 391 ConditionRecord(Uri id, ComponentName component)392 private ConditionRecord(Uri id, ComponentName component) { 393 this.id = id; 394 this.component = component; 395 } 396 397 @Override toString()398 public String toString() { 399 final StringBuilder sb = new StringBuilder("ConditionRecord[id=") 400 .append(id).append(",component=").append(component) 401 .append(",subscribed=").append(subscribed); 402 return sb.append(']').toString(); 403 } 404 } 405 406 public interface Callback { onBootComplete()407 void onBootComplete(); onServiceAdded(ComponentName component)408 void onServiceAdded(ComponentName component); onConditionChanged(Uri id, Condition condition)409 void onConditionChanged(Uri id, Condition condition); onUserSwitched()410 void onUserSwitched(); 411 } 412 413 } 414