1 /*
2  * Copyright (C) 2020 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.applications.specialaccess.notificationaccess;
18 
19 import android.app.NotificationManager;
20 import android.app.settings.SettingsEnums;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.pm.PackageInfo;
24 import android.content.pm.PackageManager;
25 import android.os.AsyncTask;
26 
27 import androidx.annotation.NonNull;
28 import androidx.annotation.VisibleForTesting;
29 import androidx.preference.Preference;
30 import androidx.preference.PreferenceFragmentCompat;
31 
32 import com.android.settings.core.BasePreferenceController;
33 import com.android.settings.overlay.FeatureFactory;
34 import com.android.settingslib.RestrictedSwitchPreference;
35 
36 public class ApprovalPreferenceController extends BasePreferenceController {
37 
38     private static final String TAG = "ApprovalPrefController";
39 
40     private PackageInfo mPkgInfo;
41     private ComponentName mCn;
42     private PreferenceFragmentCompat mParent;
43     private NotificationManager mNm;
44     private PackageManager mPm;
45     // The appOp representing this preference
46     private String mSettingIdentifier;
47 
ApprovalPreferenceController(Context context, String key)48     public ApprovalPreferenceController(Context context, String key) {
49         super(context, key);
50     }
51 
setPkgInfo(PackageInfo pkgInfo)52     public ApprovalPreferenceController setPkgInfo(PackageInfo pkgInfo) {
53         mPkgInfo = pkgInfo;
54         return this;
55     }
56 
setCn(ComponentName cn)57     public ApprovalPreferenceController setCn(ComponentName cn) {
58         mCn = cn;
59         return this;
60     }
61 
setParent(PreferenceFragmentCompat parent)62     public ApprovalPreferenceController setParent(PreferenceFragmentCompat parent) {
63         mParent = parent;
64         return this;
65     }
66 
setNm(NotificationManager nm)67     public ApprovalPreferenceController setNm(NotificationManager nm) {
68         mNm = nm;
69         return this;
70     }
71 
setPm(PackageManager pm)72     public ApprovalPreferenceController setPm(PackageManager pm) {
73         mPm = pm;
74         return this;
75     }
76 
77     /**
78      * Set the associated appOp for the Setting
79      */
80     @NonNull
setSettingIdentifier(@onNull String settingIdentifier)81     public ApprovalPreferenceController setSettingIdentifier(@NonNull String settingIdentifier) {
82         mSettingIdentifier = settingIdentifier;
83         return this;
84     }
85 
86     @Override
getAvailabilityStatus()87     public int getAvailabilityStatus() {
88         return AVAILABLE;
89     }
90 
91     @Override
updateState(Preference pref)92     public void updateState(Preference pref) {
93         final RestrictedSwitchPreference preference =
94                 (RestrictedSwitchPreference) pref;
95         final CharSequence label = mPkgInfo.applicationInfo.loadLabel(mPm);
96         final boolean isAllowedCn = mCn.flattenToShortString().length()
97                 <= NotificationManager.MAX_SERVICE_COMPONENT_NAME_LENGTH;
98         final boolean isEnabled = isServiceEnabled(mCn);
99         preference.setChecked(isEnabled);
100         preference.setOnPreferenceChangeListener((p, newValue) -> {
101             final boolean access = (Boolean) newValue;
102             if (!access) {
103                 if (!isServiceEnabled(mCn)) {
104                     return true; // already disabled
105                 }
106                 // show a friendly dialog
107                 new FriendlyWarningDialogFragment()
108                         .setServiceInfo(mCn, label, mParent)
109                         .show(mParent.getFragmentManager(), "friendlydialog");
110                 return false;
111             } else {
112                 if (isServiceEnabled(mCn)) {
113                     return true; // already enabled
114                 }
115                 // show a scary dialog
116                 new ScaryWarningDialogFragment()
117                         .setServiceInfo(mCn, label, mParent)
118                         .show(mParent.getFragmentManager(), "dialog");
119                 return false;
120             }
121         });
122 
123         if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
124                 && android.security.Flags.extendEcmToAllSettings()) {
125             if (!isAllowedCn && !isEnabled) {
126                 preference.setEnabled(false);
127             } else if (isEnabled) {
128                 preference.setEnabled(true);
129             } else {
130                 preference.checkEcmRestrictionAndSetDisabled(mSettingIdentifier,
131                         mCn.getPackageName());
132             }
133         } else {
134             preference.updateState(
135                     mCn.getPackageName(), mPkgInfo.applicationInfo.uid, isAllowedCn, isEnabled);
136         }
137     }
138 
disable(final ComponentName cn)139     public void disable(final ComponentName cn) {
140         logSpecialPermissionChange(true, cn.getPackageName());
141         mNm.setNotificationListenerAccessGranted(cn, false);
142         if (!mNm.isNotificationPolicyAccessGrantedForPackage(
143                 cn.getPackageName())) {
144             if (android.app.Flags.modesApi()) {
145                 mNm.removeAutomaticZenRules(cn.getPackageName(), /* fromUser= */ true);
146             } else {
147                 mNm.removeAutomaticZenRules(cn.getPackageName());
148             }
149         }
150     }
151 
enable(ComponentName cn)152     protected void enable(ComponentName cn) {
153         logSpecialPermissionChange(true, cn.getPackageName());
154         mNm.setNotificationListenerAccessGranted(cn, true);
155     }
156 
isServiceEnabled(ComponentName cn)157     protected boolean isServiceEnabled(ComponentName cn) {
158         return mNm.isNotificationListenerAccessGranted(cn);
159     }
160 
161     @VisibleForTesting
logSpecialPermissionChange(boolean enable, String packageName)162     void logSpecialPermissionChange(boolean enable, String packageName) {
163         final int logCategory = enable ? SettingsEnums.APP_SPECIAL_PERMISSION_NOTIVIEW_ALLOW
164                 : SettingsEnums.APP_SPECIAL_PERMISSION_NOTIVIEW_DENY;
165         FeatureFactory.getFeatureFactory().getMetricsFeatureProvider().action(mContext,
166                 logCategory, packageName);
167     }
168 }