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 
18 package com.android.settings.notification;
19 
20 import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_APPS_CANNOT_ACCESS_NOTIFICATION_SETTINGS;
21 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
22 
23 import static com.android.internal.notification.NotificationAccessConfirmationActivityContract.EXTRA_COMPONENT_NAME;
24 import static com.android.internal.notification.NotificationAccessConfirmationActivityContract.EXTRA_USER_ID;
25 
26 import android.Manifest;
27 import android.app.Activity;
28 import android.app.NotificationManager;
29 import android.app.admin.DevicePolicyManager;
30 import android.content.ComponentName;
31 import android.content.Context;
32 import android.content.DialogInterface;
33 import android.content.pm.ApplicationInfo;
34 import android.content.pm.PackageItemInfo;
35 import android.content.pm.PackageManager;
36 import android.content.pm.ServiceInfo;
37 import android.os.Bundle;
38 import android.os.UserHandle;
39 import android.os.UserManager;
40 import android.text.TextUtils;
41 import android.util.Slog;
42 import android.view.WindowManager;
43 import android.view.accessibility.AccessibilityEvent;
44 import android.widget.Toast;
45 
46 import androidx.annotation.Nullable;
47 
48 import com.android.internal.app.AlertActivity;
49 import com.android.internal.app.AlertController;
50 import com.android.settings.R;
51 
52 /** @hide */
53 public class NotificationAccessConfirmationActivity extends Activity
54         implements DialogInterface {
55 
56     private static final boolean DEBUG = false;
57     private static final String LOG_TAG = "NotificationAccessConfirmationActivity";
58 
59     private int mUserId;
60     private ComponentName mComponentName;
61     private NotificationManager mNm;
62 
63     private DevicePolicyManager mDpm;
64     private UserManager mUm;
65 
66     @Override
onCreate(@ullable Bundle savedInstanceState)67     protected void onCreate(@Nullable Bundle savedInstanceState) {
68         super.onCreate(savedInstanceState);
69 
70         getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
71 
72         mUm = getSystemService(UserManager.class);
73         mDpm = getSystemService(DevicePolicyManager.class);
74 
75         if (mUm.isManagedProfile()) {
76             Slog.w(LOG_TAG, "Apps in the work profile do not support notification listeners");
77             Toast.makeText(this,
78                     mDpm.getResources().getString(WORK_APPS_CANNOT_ACCESS_NOTIFICATION_SETTINGS,
79                             () -> getString(R.string.notification_settings_work_profile)),
80                     Toast.LENGTH_SHORT).show();
81             finish();
82             return;
83         }
84 
85         mNm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
86 
87         mComponentName = getIntent().getParcelableExtra(EXTRA_COMPONENT_NAME);
88         mUserId = getIntent().getIntExtra(EXTRA_USER_ID, UserHandle.USER_NULL);
89         CharSequence mAppLabel;
90 
91         if (mComponentName == null || mComponentName.getPackageName() == null
92                 || mComponentName.flattenToString().length()
93                 > NotificationManager.MAX_SERVICE_COMPONENT_NAME_LENGTH) {
94             finish();
95             return;
96         }
97 
98         try {
99             ApplicationInfo applicationInfo = getPackageManager().getApplicationInfo(
100                     mComponentName.getPackageName(), 0);
101             mAppLabel = applicationInfo.loadSafeLabel(getPackageManager(),
102                     PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX,
103                     PackageItemInfo.SAFE_LABEL_FLAG_TRIM
104                             | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
105         } catch (PackageManager.NameNotFoundException e) {
106             Slog.e(LOG_TAG, "Couldn't find app with package name for " + mComponentName, e);
107             finish();
108             return;
109         }
110 
111         if (TextUtils.isEmpty(mAppLabel)) {
112             finish();
113             return;
114         }
115 
116         AlertController.AlertParams p = new AlertController.AlertParams(this);
117         p.mTitle = getString(
118                 R.string.notification_listener_security_warning_title,
119                 mAppLabel);
120         p.mMessage = getString(
121                 R.string.notification_listener_security_warning_summary,
122                 mAppLabel);
123         p.mPositiveButtonText = getString(R.string.allow);
124         p.mPositiveButtonListener = (a, b) -> onAllow();
125         p.mNegativeButtonText = getString(R.string.deny);
126         p.mNegativeButtonListener = (a, b) -> cancel();
127         AlertController
128                 .create(this, this, getWindow())
129                 .installContent(p);
130         // Consistent with the permission dialog
131         // Used instead of p.mCancelable as that is only honored for AlertDialog
132         getWindow().setCloseOnTouchOutside(false);
133     }
134 
135     @Override
onResume()136     public void onResume() {
137         super.onResume();
138         getWindow().addFlags(
139                 WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
140     }
141 
142     @Override
onPause()143     public void onPause() {
144         getWindow().clearFlags(
145                 WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
146         super.onPause();
147     }
148 
onAllow()149     private void onAllow() {
150         String requiredPermission = Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
151         try {
152             ServiceInfo serviceInfo = getPackageManager().getServiceInfo(mComponentName, 0);
153             if (!requiredPermission.equals(serviceInfo.permission)) {
154                 Slog.e(LOG_TAG,
155                         "Service " + mComponentName + " lacks permission " + requiredPermission);
156                 return;
157             }
158         } catch (PackageManager.NameNotFoundException e) {
159             Slog.e(LOG_TAG, "Failed to get service info for " + mComponentName, e);
160             return;
161         }
162 
163         mNm.setNotificationListenerAccessGranted(mComponentName, true);
164 
165         finish();
166     }
167 
168     @Override
dispatchPopulateAccessibilityEvent(AccessibilityEvent event)169     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
170         return AlertActivity.dispatchPopulateAccessibilityEvent(this, event);
171     }
172 
173     @Override
onBackPressed()174     public void onBackPressed() {
175         // Suppress finishing the activity on back button press,
176         // consistently with the permission dialog behavior
177     }
178 
179     @Override
cancel()180     public void cancel() {
181         finish();
182     }
183 
184     @Override
dismiss()185     public void dismiss() {
186         // This is called after the click, since we finish when handling the
187         // click, don't do that again here.
188         if (!isFinishing()) {
189             finish();
190         }
191     }
192 }
193