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