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