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.systemui.statusbar.policy; 18 19 import android.app.AlarmManager; 20 import android.app.NotificationManager; 21 import android.content.BroadcastReceiver; 22 import android.content.ComponentName; 23 import android.content.ContentResolver; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.database.ContentObserver; 28 import android.net.Uri; 29 import android.os.Handler; 30 import android.os.HandlerExecutor; 31 import android.os.Trace; 32 import android.os.UserHandle; 33 import android.os.UserManager; 34 import android.provider.Settings.Global; 35 import android.provider.Settings.Secure; 36 import android.service.notification.ZenModeConfig; 37 import android.service.notification.ZenModeConfig.ZenRule; 38 import android.text.format.DateFormat; 39 import android.util.Log; 40 41 import androidx.annotation.NonNull; 42 43 import com.android.internal.annotations.VisibleForTesting; 44 import com.android.systemui.Dumpable; 45 import com.android.systemui.broadcast.BroadcastDispatcher; 46 import com.android.systemui.dagger.SysUISingleton; 47 import com.android.systemui.dagger.qualifiers.Main; 48 import com.android.systemui.dump.DumpManager; 49 import com.android.systemui.settings.UserTracker; 50 import com.android.systemui.util.Utils; 51 import com.android.systemui.util.settings.GlobalSettings; 52 53 import java.io.PrintWriter; 54 import java.util.ArrayList; 55 import java.util.Objects; 56 57 import javax.inject.Inject; 58 59 /** Platform implementation of the zen mode controller. **/ 60 @SysUISingleton 61 public class ZenModeControllerImpl implements ZenModeController, Dumpable { 62 private static final String TAG = "ZenModeController"; 63 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 64 65 private final ArrayList<Callback> mCallbacks = new ArrayList<>(); 66 private final Object mCallbacksLock = new Object(); 67 private final Context mContext; 68 private final UserTracker mUserTracker; 69 private final BroadcastDispatcher mBroadcastDispatcher; 70 private final NotificationManager mNoMan; 71 private final AlarmManager mAlarmManager; 72 private final SetupObserver mSetupObserver; 73 private final UserManager mUserManager; 74 private final GlobalSettings mGlobalSettings; 75 76 private int mUserId; 77 private boolean mRegistered; 78 private ZenModeConfig mConfig; 79 // This value is changed in the main thread, but may be read in a background thread. 80 private volatile int mZenMode; 81 private long mZenUpdateTime; 82 private NotificationManager.Policy mConsolidatedNotificationPolicy; 83 84 private final UserTracker.Callback mUserChangedCallback = 85 new UserTracker.Callback() { 86 @Override 87 public void onUserChanged(int newUser, Context userContext) { 88 mUserId = newUser; 89 if (mRegistered) { 90 mBroadcastDispatcher.unregisterReceiver(mReceiver); 91 } 92 final IntentFilter filter = new IntentFilter( 93 AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED); 94 filter.addAction(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED); 95 mBroadcastDispatcher.registerReceiver(mReceiver, filter, null, 96 UserHandle.of(mUserId)); 97 mRegistered = true; 98 mSetupObserver.register(); 99 } 100 }; 101 102 @Inject ZenModeControllerImpl( Context context, @Main Handler handler, BroadcastDispatcher broadcastDispatcher, DumpManager dumpManager, GlobalSettings globalSettings, UserTracker userTracker)103 public ZenModeControllerImpl( 104 Context context, 105 @Main Handler handler, 106 BroadcastDispatcher broadcastDispatcher, 107 DumpManager dumpManager, 108 GlobalSettings globalSettings, 109 UserTracker userTracker) { 110 mContext = context; 111 mBroadcastDispatcher = broadcastDispatcher; 112 mUserTracker = userTracker; 113 mGlobalSettings = globalSettings; 114 115 ContentObserver modeContentObserver = new ContentObserver(handler) { 116 @Override 117 public void onChange(boolean selfChange) { 118 int value = getModeSettingValueFromProvider(); 119 Log.d(TAG, "Zen mode setting changed to " + value); 120 updateZenMode(value); 121 fireZenChanged(value); 122 } 123 }; 124 ContentObserver configContentObserver = new ContentObserver(handler) { 125 @Override 126 public void onChange(boolean selfChange) { 127 try { 128 Trace.beginSection("updateZenModeConfig"); 129 updateZenModeConfig(); 130 } finally { 131 Trace.endSection(); 132 } 133 } 134 }; 135 mNoMan = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 136 globalSettings.registerContentObserver(Global.ZEN_MODE, modeContentObserver); 137 updateZenMode(getModeSettingValueFromProvider()); 138 globalSettings.registerContentObserver(Global.ZEN_MODE_CONFIG_ETAG, configContentObserver); 139 updateZenModeConfig(); 140 updateConsolidatedNotificationPolicy(); 141 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 142 mSetupObserver = new SetupObserver(handler); 143 mSetupObserver.register(); 144 mUserManager = context.getSystemService(UserManager.class); 145 mUserTracker.addCallback(mUserChangedCallback, new HandlerExecutor(handler)); 146 147 dumpManager.registerDumpable(getClass().getSimpleName(), this); 148 } 149 getModeSettingValueFromProvider()150 private int getModeSettingValueFromProvider() { 151 return mGlobalSettings.getInt(Global.ZEN_MODE, /* default */ Global.ZEN_MODE_OFF); 152 } 153 154 @Override isVolumeRestricted()155 public boolean isVolumeRestricted() { 156 return mUserManager.hasUserRestriction(UserManager.DISALLOW_ADJUST_VOLUME, 157 UserHandle.of(mUserId)); 158 } 159 160 @Override areNotificationsHiddenInShade()161 public boolean areNotificationsHiddenInShade() { 162 if (mZenMode != Global.ZEN_MODE_OFF) { 163 return (mConsolidatedNotificationPolicy.suppressedVisualEffects 164 & NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST) != 0; 165 } 166 return false; 167 } 168 169 @Override addCallback(@onNull Callback callback)170 public void addCallback(@NonNull Callback callback) { 171 synchronized (mCallbacksLock) { 172 mCallbacks.add(callback); 173 } 174 } 175 176 @Override removeCallback(@onNull Callback callback)177 public void removeCallback(@NonNull Callback callback) { 178 synchronized (mCallbacksLock) { 179 mCallbacks.remove(callback); 180 } 181 } 182 183 @Override getZen()184 public int getZen() { 185 return mZenMode; 186 } 187 188 @Override setZen(int zen, Uri conditionId, String reason)189 public void setZen(int zen, Uri conditionId, String reason) { 190 mNoMan.setZenMode(zen, conditionId, reason); 191 } 192 193 @Override isZenAvailable()194 public boolean isZenAvailable() { 195 return mSetupObserver.isDeviceProvisioned() && mSetupObserver.isUserSetup(); 196 } 197 198 @Override getManualRule()199 public ZenRule getManualRule() { 200 return mConfig == null ? null : mConfig.manualRule; 201 } 202 203 @Override getConfig()204 public ZenModeConfig getConfig() { 205 return mConfig; 206 } 207 208 @Override getConsolidatedPolicy()209 public NotificationManager.Policy getConsolidatedPolicy() { 210 return mConsolidatedNotificationPolicy; 211 } 212 213 @Override getNextAlarm()214 public long getNextAlarm() { 215 final AlarmManager.AlarmClockInfo info = mAlarmManager.getNextAlarmClock(mUserId); 216 return info != null ? info.getTriggerTime() : 0; 217 } 218 219 @Override getEffectsSuppressor()220 public ComponentName getEffectsSuppressor() { 221 return NotificationManager.from(mContext).getEffectsSuppressor(); 222 } 223 224 @Override isCountdownConditionSupported()225 public boolean isCountdownConditionSupported() { 226 return NotificationManager.from(mContext) 227 .isSystemConditionProviderEnabled(ZenModeConfig.COUNTDOWN_PATH); 228 } 229 230 @Override getCurrentUser()231 public int getCurrentUser() { 232 return mUserTracker.getUserId(); 233 } 234 fireNextAlarmChanged()235 private void fireNextAlarmChanged() { 236 synchronized (mCallbacksLock) { 237 Utils.safeForeach(mCallbacks, c -> c.onNextAlarmChanged()); 238 } 239 } 240 fireEffectsSuppressorChanged()241 private void fireEffectsSuppressorChanged() { 242 synchronized (mCallbacksLock) { 243 Utils.safeForeach(mCallbacks, c -> c.onEffectsSupressorChanged()); 244 } 245 } 246 fireZenChanged(int zen)247 private void fireZenChanged(int zen) { 248 synchronized (mCallbacksLock) { 249 Utils.safeForeach(mCallbacks, c -> c.onZenChanged(zen)); 250 } 251 } 252 fireZenAvailableChanged(boolean available)253 private void fireZenAvailableChanged(boolean available) { 254 synchronized (mCallbacksLock) { 255 Utils.safeForeach(mCallbacks, c -> c.onZenAvailableChanged(available)); 256 } 257 } 258 fireManualRuleChanged(ZenRule rule)259 private void fireManualRuleChanged(ZenRule rule) { 260 synchronized (mCallbacksLock) { 261 Utils.safeForeach(mCallbacks, c -> c.onManualRuleChanged(rule)); 262 } 263 } 264 fireConsolidatedPolicyChanged(NotificationManager.Policy policy)265 private void fireConsolidatedPolicyChanged(NotificationManager.Policy policy) { 266 synchronized (mCallbacksLock) { 267 Utils.safeForeach(mCallbacks, c -> c.onConsolidatedPolicyChanged(policy)); 268 } 269 } 270 271 @VisibleForTesting fireConfigChanged(ZenModeConfig config)272 protected void fireConfigChanged(ZenModeConfig config) { 273 synchronized (mCallbacksLock) { 274 Utils.safeForeach(mCallbacks, c -> c.onConfigChanged(config)); 275 } 276 } 277 278 @VisibleForTesting updateZenMode(int mode)279 protected void updateZenMode(int mode) { 280 mZenMode = mode; 281 mZenUpdateTime = System.currentTimeMillis(); 282 } 283 284 @VisibleForTesting updateConsolidatedNotificationPolicy()285 protected void updateConsolidatedNotificationPolicy() { 286 final NotificationManager.Policy policy = mNoMan.getConsolidatedNotificationPolicy(); 287 if (!Objects.equals(policy, mConsolidatedNotificationPolicy)) { 288 mConsolidatedNotificationPolicy = policy; 289 fireConsolidatedPolicyChanged(policy); 290 } 291 } 292 293 @VisibleForTesting updateZenModeConfig()294 protected void updateZenModeConfig() { 295 final ZenModeConfig config = mNoMan.getZenModeConfig(); 296 if (Objects.equals(config, mConfig)) return; 297 final ZenRule oldRule = mConfig != null ? mConfig.manualRule : null; 298 mConfig = config; 299 mZenUpdateTime = System.currentTimeMillis(); 300 fireConfigChanged(config); 301 302 final ZenRule newRule = config != null ? config.manualRule : null; 303 if (!Objects.equals(oldRule, newRule)) { 304 fireManualRuleChanged(newRule); 305 } 306 307 final NotificationManager.Policy consolidatedPolicy = 308 mNoMan.getConsolidatedNotificationPolicy(); 309 if (!Objects.equals(consolidatedPolicy, mConsolidatedNotificationPolicy)) { 310 mConsolidatedNotificationPolicy = consolidatedPolicy; 311 fireConsolidatedPolicyChanged(consolidatedPolicy); 312 } 313 314 } 315 316 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 317 @Override 318 public void onReceive(Context context, Intent intent) { 319 if (AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED.equals(intent.getAction())) { 320 fireNextAlarmChanged(); 321 } 322 if (NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED.equals(intent.getAction())) { 323 fireEffectsSuppressorChanged(); 324 } 325 } 326 }; 327 328 @Override dump(PrintWriter pw, String[] args)329 public void dump(PrintWriter pw, String[] args) { 330 pw.println("ZenModeControllerImpl:"); 331 pw.println(" mZenMode=" + mZenMode); 332 pw.println(" mConfig=" + mConfig); 333 pw.println(" mConsolidatedNotificationPolicy=" + mConsolidatedNotificationPolicy); 334 pw.println(" mZenUpdateTime=" + DateFormat.format("MM-dd HH:mm:ss", mZenUpdateTime)); 335 } 336 337 private final class SetupObserver extends ContentObserver { 338 private final ContentResolver mResolver; 339 340 private boolean mRegistered; 341 SetupObserver(Handler handler)342 public SetupObserver(Handler handler) { 343 super(handler); 344 mResolver = mContext.getContentResolver(); 345 } 346 isUserSetup()347 public boolean isUserSetup() { 348 return Secure.getIntForUser(mResolver, Secure.USER_SETUP_COMPLETE, 0, mUserId) != 0; 349 } 350 isDeviceProvisioned()351 public boolean isDeviceProvisioned() { 352 return Global.getInt(mResolver, Global.DEVICE_PROVISIONED, 0) != 0; 353 } 354 register()355 public void register() { 356 if (mRegistered) { 357 mResolver.unregisterContentObserver(this); 358 } 359 mResolver.registerContentObserver( 360 Global.getUriFor(Global.DEVICE_PROVISIONED), false, this); 361 mResolver.registerContentObserver( 362 Secure.getUriFor(Secure.USER_SETUP_COMPLETE), false, this, mUserId); 363 mRegistered = true; 364 fireZenAvailableChanged(isZenAvailable()); 365 } 366 367 @Override onChange(boolean selfChange, Uri uri)368 public void onChange(boolean selfChange, Uri uri) { 369 if (Global.getUriFor(Global.DEVICE_PROVISIONED).equals(uri) 370 || Secure.getUriFor(Secure.USER_SETUP_COMPLETE).equals(uri)) { 371 fireZenAvailableChanged(isZenAvailable()); 372 } 373 } 374 } 375 } 376