1 /*
2  * Copyright (C) 2017 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.settings.notification.zen;
18 
19 import android.app.AutomaticZenRule;
20 import android.app.NotificationManager;
21 import android.app.settings.SettingsEnums;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.pm.ActivityInfo;
26 import android.content.pm.ComponentInfo;
27 import android.content.pm.PackageManager;
28 import android.content.pm.ServiceInfo;
29 import android.os.Binder;
30 import android.provider.Settings;
31 import android.service.notification.ConditionProviderService;
32 import android.util.Log;
33 import android.util.Slog;
34 
35 import androidx.fragment.app.Fragment;
36 import androidx.preference.Preference;
37 
38 import com.android.settings.core.PreferenceControllerMixin;
39 import com.android.settingslib.core.lifecycle.Lifecycle;
40 
41 import java.util.Map;
42 import java.util.Objects;
43 
44 abstract public class AbstractZenModeAutomaticRulePreferenceController extends
45         AbstractZenModePreferenceController implements PreferenceControllerMixin {
46 
47     protected ZenModeBackend mBackend;
48     protected Fragment mParent;
49     protected Map.Entry<String, AutomaticZenRule>[] mRules;
50     protected PackageManager mPm;
51 
AbstractZenModeAutomaticRulePreferenceController(Context context, String key, Fragment parent, Lifecycle lifecycle)52     public AbstractZenModeAutomaticRulePreferenceController(Context context, String key, Fragment
53             parent, Lifecycle lifecycle) {
54         super(context, key, lifecycle);
55         mBackend = ZenModeBackend.getInstance(context);
56         mPm = mContext.getPackageManager();
57         mParent = parent;
58     }
59 
60     @Override
updateState(Preference preference)61     public void updateState(Preference preference) {
62         super.updateState(preference);
63         mRules = mBackend.getAutomaticZenRules();
64     }
65 
getRules()66     protected Map.Entry<String, AutomaticZenRule>[] getRules() {
67         if (mRules == null) {
68             mRules = mBackend.getAutomaticZenRules();
69         }
70         return mRules;
71     }
72 
showNameRuleDialog(final ZenRuleInfo ri, Fragment parent)73     protected void showNameRuleDialog(final ZenRuleInfo ri, Fragment parent) {
74         ZenRuleNameDialog.show(parent, null, ri.defaultConditionId, new
75                 RuleNameChangeListener(ri));
76     }
77 
getRuleIntent(String settingsAction, ComponentName configurationActivity, String ruleId)78     protected static Intent getRuleIntent(String settingsAction,
79             ComponentName configurationActivity, String ruleId) {
80         final Intent intent = new Intent()
81                 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
82                 .putExtra(ConditionProviderService.EXTRA_RULE_ID, ruleId);
83         if (configurationActivity != null) {
84             intent.setComponent(configurationActivity);
85         } else {
86             intent.setAction(settingsAction);
87         }
88         return intent;
89     }
90 
getRuleInfo(PackageManager pm, ComponentInfo ci)91     public static ZenRuleInfo getRuleInfo(PackageManager pm, ComponentInfo ci) {
92         if (ci == null || ci.metaData == null) {
93             return null;
94         }
95         final String ruleType = (ci instanceof ServiceInfo)
96                 ? ci.metaData.getString(ConditionProviderService.META_DATA_RULE_TYPE)
97                 : ci.metaData.getString(NotificationManager.META_DATA_AUTOMATIC_RULE_TYPE);
98 
99         final ComponentName configurationActivity = getSettingsActivity(pm, null, ci);
100         if (ruleType != null && !ruleType.trim().isEmpty() && configurationActivity != null) {
101             final ZenRuleInfo ri = new ZenRuleInfo();
102             ri.serviceComponent =
103                     (ci instanceof ServiceInfo) ? new ComponentName(ci.packageName, ci.name) : null;
104             ri.settingsAction = Settings.ACTION_ZEN_MODE_EXTERNAL_RULE_SETTINGS;
105             ri.title = ruleType;
106             ri.packageName = ci.packageName;
107             ri.configurationActivity = configurationActivity;
108             ri.packageLabel = ci.applicationInfo.loadLabel(pm);
109             ri.ruleInstanceLimit = (ci instanceof ServiceInfo)
110                     ? ci.metaData.getInt(ConditionProviderService.META_DATA_RULE_INSTANCE_LIMIT, -1)
111                     : ci.metaData.getInt(NotificationManager.META_DATA_RULE_INSTANCE_LIMIT, -1);
112             return ri;
113         }
114         return null;
115     }
116 
getSettingsActivity(PackageManager pm, AutomaticZenRule rule, ComponentInfo ci)117     protected static ComponentName getSettingsActivity(PackageManager pm, AutomaticZenRule rule,
118             ComponentInfo ci) {
119         String owner = rule != null ? rule.getPackageName() : ci.packageName;
120         ComponentName settingsActivity = null;
121         // prefer config activity on the rule itself; fallback to manifest definition
122         if (rule != null && rule.getConfigurationActivity() != null) {
123             settingsActivity = rule.getConfigurationActivity();
124         } else {
125             if (ci == null) {
126                 settingsActivity = null;
127             } else if (ci instanceof ActivityInfo) {
128                 // new activity backed rule
129                 settingsActivity = new ComponentName(ci.packageName, ci.name);
130             } else if (ci.metaData != null) {
131                 // old service backed rule
132                 final String configurationActivity = ci.metaData.getString(
133                         ConditionProviderService.META_DATA_CONFIGURATION_ACTIVITY);
134                 if (configurationActivity != null) {
135                     settingsActivity = ComponentName.unflattenFromString(configurationActivity);
136                 }
137             }
138         }
139         if (settingsActivity == null || owner == null) {
140             return settingsActivity;
141         }
142         try {
143             int ownerUid = pm.getPackageUid(owner, 0);
144             int configActivityOwnerUid = pm.getPackageUid(settingsActivity.getPackageName(), 0);
145             if (ownerUid == configActivityOwnerUid) {
146                 return settingsActivity;
147             } else {
148                 Log.w(TAG, "Config activity not in owner package for " + rule.getName());
149                 return null;
150             }
151         } catch (PackageManager.NameNotFoundException e) {
152             Log.e(TAG, "Failed to find config activity");
153             return null;
154         }
155     }
156 
157     public class RuleNameChangeListener implements ZenRuleNameDialog.PositiveClickListener {
158         ZenRuleInfo mRuleInfo;
159 
RuleNameChangeListener(ZenRuleInfo ruleInfo)160         public RuleNameChangeListener(ZenRuleInfo ruleInfo) {
161             mRuleInfo = ruleInfo;
162         }
163 
164         @Override
onOk(String ruleName, Fragment parent)165         public void onOk(String ruleName, Fragment parent) {
166             mMetricsFeatureProvider.action(mContext,
167                     SettingsEnums.ACTION_ZEN_MODE_RULE_NAME_CHANGE_OK);
168             AutomaticZenRule rule = new AutomaticZenRule(ruleName, mRuleInfo.serviceComponent,
169                     mRuleInfo.configurationActivity, mRuleInfo.defaultConditionId, null,
170                     NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
171             String savedRuleId = mBackend.addZenRule(rule);
172             if (savedRuleId != null) {
173                 parent.startActivity(getRuleIntent(mRuleInfo.settingsAction, null,
174                         savedRuleId));
175             }
176         }
177     }
178 }
179