import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Objects;
private final Context mContext;
private final H mHandler;
private final SettingsObserver mSettingsObserver;
- @VisibleForTesting protected final AppOpsManager mAppOps;
+ private final AppOpsManager mAppOps;
@VisibleForTesting protected final NotificationManager mNotificationManager;
@VisibleForTesting protected ZenModeConfig mDefaultConfig;
private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
@VisibleForTesting protected boolean mIsBootComplete;
+ private String[] mPriorityOnlyDndExemptPackages;
+
public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders) {
mContext = context;
mHandler = new H(looper);
addCallback(mMetrics);
- mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ mAppOps = context.getSystemService(AppOpsManager.class);
mNotificationManager = context.getSystemService(NotificationManager.class);
mDefaultConfig = readDefaultConfig(mContext.getResources());
loadConfigForUser(user, "onUserUnlocked");
}
+ void setPriorityOnlyDndExemptPackages(String[] packages) {
+ mPriorityOnlyDndExemptPackages = packages;
+ }
+
private void loadConfigForUser(int user, String reason) {
if (mUser == user || user < UserHandle.USER_SYSTEM) return;
mUser = user;
for (int usage : AudioAttributes.SDK_USAGES) {
final int suppressionBehavior = AudioAttributes.SUPPRESSIBLE_USAGES.get(usage);
if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_NEVER) {
- applyRestrictions(false /*mute*/, usage);
+ applyRestrictions(zenPriorityOnly, false /*mute*/, usage);
} else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_NOTIFICATION) {
- applyRestrictions(muteNotifications || muteEverything, usage);
+ applyRestrictions(zenPriorityOnly, muteNotifications || muteEverything, usage);
} else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_CALL) {
- applyRestrictions(muteCalls || muteEverything, usage);
+ applyRestrictions(zenPriorityOnly, muteCalls || muteEverything, usage);
} else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_ALARM) {
- applyRestrictions(muteAlarms || muteEverything, usage);
+ applyRestrictions(zenPriorityOnly, muteAlarms || muteEverything, usage);
} else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_MEDIA) {
- applyRestrictions(muteMedia || muteEverything, usage);
+ applyRestrictions(zenPriorityOnly, muteMedia || muteEverything, usage);
} else if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_SYSTEM) {
if (usage == AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) {
// normally DND will only restrict touch sounds, not haptic feedback/vibrations
- applyRestrictions(muteSystem || muteEverything, usage,
+ applyRestrictions(zenPriorityOnly, muteSystem || muteEverything, usage,
AppOpsManager.OP_PLAY_AUDIO);
- applyRestrictions(false, usage, AppOpsManager.OP_VIBRATE);
+ applyRestrictions(zenPriorityOnly, false, usage, AppOpsManager.OP_VIBRATE);
} else {
- applyRestrictions(muteSystem || muteEverything, usage);
+ applyRestrictions(zenPriorityOnly, muteSystem || muteEverything, usage);
}
} else {
- applyRestrictions(muteEverything, usage);
+ applyRestrictions(zenPriorityOnly, muteEverything, usage);
}
}
}
@VisibleForTesting
- protected void applyRestrictions(boolean mute, int usage, int code) {
- final String[] exceptionPackages = null; // none (for now)
-
- // Only do this if we are executing within the system process... otherwise
- // we are running as test code, so don't have access to the protected call.
- if (Process.myUid() == Process.SYSTEM_UID) {
- final long ident = Binder.clearCallingIdentity();
- try {
- mAppOps.setRestriction(code, usage,
- mute ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
- exceptionPackages);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
+ protected void applyRestrictions(boolean zenPriorityOnly, boolean mute, int usage, int code) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mAppOps.setRestriction(code, usage,
+ mute ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
+ zenPriorityOnly ? mPriorityOnlyDndExemptPackages : null);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
@VisibleForTesting
- protected void applyRestrictions(boolean mute, int usage) {
- applyRestrictions(mute, usage, AppOpsManager.OP_VIBRATE);
- applyRestrictions(mute, usage, AppOpsManager.OP_PLAY_AUDIO);
+ protected void applyRestrictions(boolean zenPriorityOnly, boolean mute, int usage) {
+ applyRestrictions(zenPriorityOnly, mute, usage, AppOpsManager.OP_VIBRATE);
+ applyRestrictions(zenPriorityOnly, mute, usage, AppOpsManager.OP_PLAY_AUDIO);
}
private ZenModeHelper mZenModeHelperSpy;
private Context mContext;
private ContentResolver mContentResolver;
+ @Mock AppOpsManager mAppOps;
@Before
public void setUp() {
e.toString());
}
+ when(mContext.getSystemService(AppOpsManager.class)).thenReturn(mAppOps);
when(mContext.getSystemService(NotificationManager.class)).thenReturn(mNotificationManager);
mConditionProviders = new ConditionProviders(mContext, new UserProfiles(),
AppGlobals.getPackageManager());
Policy.PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0);
mZenModeHelperSpy.applyRestrictions();
- doNothing().when(mZenModeHelperSpy).applyRestrictions(anyBoolean(), anyInt());
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+ doNothing().when(mZenModeHelperSpy).applyRestrictions(eq(false), anyBoolean(), anyInt());
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false,
AudioAttributes.USAGE_ALARM);
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false,
AudioAttributes.USAGE_MEDIA);
}
Policy.PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0);
mZenModeHelperSpy.applyRestrictions();
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, false,
AudioAttributes.USAGE_ALARM);
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, false,
AudioAttributes.USAGE_MEDIA);
}
mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
mZenModeHelperSpy.applyRestrictions();
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, true,
AudioAttributes.USAGE_ALARM);
// Media is a catch-all that includes games
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, true,
AudioAttributes.USAGE_MEDIA);
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, true,
AudioAttributes.USAGE_GAME);
}
mZenModeHelperSpy.applyRestrictions();
// Total silence will silence alarms, media and system noises (but not vibrations)
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true,
AudioAttributes.USAGE_ALARM);
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true,
AudioAttributes.USAGE_MEDIA);
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true,
AudioAttributes.USAGE_GAME);
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true,
AudioAttributes.USAGE_ASSISTANCE_SONIFICATION, AppOpsManager.OP_PLAY_AUDIO);
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false,
AudioAttributes.USAGE_ASSISTANCE_SONIFICATION, AppOpsManager.OP_VIBRATE);
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true,
AudioAttributes.USAGE_UNKNOWN);
}
mZenModeHelperSpy.applyRestrictions();
// Alarms only mode will not silence alarms
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false,
AudioAttributes.USAGE_ALARM);
// Alarms only mode will not silence media
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false,
AudioAttributes.USAGE_MEDIA);
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false,
AudioAttributes.USAGE_GAME);
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false,
AudioAttributes.USAGE_UNKNOWN);
// Alarms only will silence system noises (but not vibrations)
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true,
AudioAttributes.USAGE_ASSISTANCE_SONIFICATION, AppOpsManager.OP_PLAY_AUDIO);
}
mZenModeHelperSpy.applyRestrictions();
// Alarms only mode will silence calls despite priority-mode config
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true,
AudioAttributes.USAGE_NOTIFICATION_RINGTONE);
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, true,
AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST);
}
mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
mZenModeHelperSpy.applyRestrictions();
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false,
AudioAttributes.USAGE_ALARM);
}
for (int usage : AudioAttributes.SDK_USAGES) {
if (usage == AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) {
// only mute audio, not vibrations
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, usage,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, true, usage,
AppOpsManager.OP_PLAY_AUDIO);
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, usage,
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, false, usage,
AppOpsManager.OP_VIBRATE);
} else {
boolean shouldMute = AudioAttributes.SUPPRESSIBLE_USAGES.get(usage)
!= AudioAttributes.SUPPRESSIBLE_NEVER;
- verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(shouldMute, usage);
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, shouldMute, usage);
}
}
}
@Test
+ public void testApplyRestrictions_whitelist_priorityOnlyMode() {
+ mZenModeHelperSpy.setPriorityOnlyDndExemptPackages(new String[] {PKG_O});
+ mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
+ mZenModeHelperSpy.applyRestrictions();
+
+ for (int usage : AudioAttributes.SDK_USAGES) {
+ verify(mAppOps).setRestriction(
+ eq(AppOpsManager.OP_PLAY_AUDIO), eq(usage), anyInt(), eq(new String[]{PKG_O}));
+ verify(mAppOps).setRestriction(
+ eq(AppOpsManager.OP_VIBRATE), eq(usage), anyInt(), eq(new String[]{PKG_O}));
+ }
+ }
+
+ @Test
+ public void testApplyRestrictions_whitelist_alarmsOnlyMode() {
+ mZenModeHelperSpy.setPriorityOnlyDndExemptPackages(new String[] {PKG_O});
+ mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_ALARMS;
+ mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
+ mZenModeHelperSpy.applyRestrictions();
+
+ for (int usage : AudioAttributes.SDK_USAGES) {
+ verify(mAppOps).setRestriction(
+ eq(AppOpsManager.OP_PLAY_AUDIO), eq(usage), anyInt(), eq(null));
+ verify(mAppOps).setRestriction(
+ eq(AppOpsManager.OP_VIBRATE), eq(usage), anyInt(), eq(null));
+ }
+ }
+
+ @Test
+ public void testApplyRestrictions_whitelist_totalSilenceMode() {
+ mZenModeHelperSpy.setPriorityOnlyDndExemptPackages(new String[] {PKG_O});
+ mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_NO_INTERRUPTIONS;
+ mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
+ mZenModeHelperSpy.applyRestrictions();
+
+ for (int usage : AudioAttributes.SDK_USAGES) {
+ verify(mAppOps).setRestriction(
+ eq(AppOpsManager.OP_PLAY_AUDIO), eq(usage), anyInt(), eq(null));
+ verify(mAppOps).setRestriction(
+ eq(AppOpsManager.OP_VIBRATE), eq(usage), anyInt(), eq(null));
+ }
+ }
+
+ @Test
public void testZenUpgradeNotification() {
// shows zen upgrade notification if stored settings says to shows,
// zen has not been updated, boot is completed