• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.car.settings.notifications;
19 
20 
21 import static com.android.internal.notification.NotificationAccessConfirmationActivityContract.EXTRA_COMPONENT_NAME;
22 import static com.android.internal.notification.NotificationAccessConfirmationActivityContract.EXTRA_USER_ID;
23 
24 import android.Manifest;
25 import android.annotation.Nullable;
26 import android.app.NotificationManager;
27 import android.content.ComponentName;
28 import android.content.Intent;
29 import android.content.pm.ApplicationInfo;
30 import android.content.pm.PackageItemInfo;
31 import android.content.pm.PackageManager;
32 import android.content.pm.ResolveInfo;
33 import android.os.Bundle;
34 import android.os.UserHandle;
35 import android.service.notification.NotificationListenerService;
36 import android.text.TextUtils;
37 import android.view.accessibility.AccessibilityEvent;
38 
39 import androidx.fragment.app.Fragment;
40 import androidx.fragment.app.FragmentActivity;
41 
42 import com.android.car.settings.R;
43 import com.android.car.settings.common.ConfirmationDialogFragment;
44 import com.android.car.settings.common.Logger;
45 import com.android.internal.app.AlertActivity;
46 import com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin;
47 
48 import java.util.List;
49 
50 /**
51  * This activity is a copy of
52  * {@link com.android.settings.notification.NotificationAccessConfirmationActivity}.
53  */
54 public class NotificationAccessConfirmationActivity extends FragmentActivity {
55     private static final Logger LOG = new Logger(NotificationAccessConfirmationActivity.class);
56     private static final String REQUIRED_PERMISSION =
57             Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
58 
59     private ComponentName mComponentName;
60 
61     private final ConfirmationDialogFragment.ConfirmListener mConfirmListener =
62             arguments -> onAllow();
63 
64     private final ConfirmationDialogFragment.RejectListener mRejectListener =
65             arguments -> cancel();
66 
67     @Override
onCreate(@ullable Bundle savedInstanceState)68     protected void onCreate(@Nullable Bundle savedInstanceState) {
69         super.onCreate(savedInstanceState);
70         getLifecycle().addObserver(new HideNonSystemOverlayMixin(this));
71         ConfirmationDialogFragment.resetListeners(findConfirmationDialogFragment(),
72                 mConfirmListener,
73                 mRejectListener,
74                 /* neutralListener= */ null);
75 
76         mComponentName = getIntent().getParcelableExtra(EXTRA_COMPONENT_NAME);
77         CharSequence mAppLabel;
78 
79         if (mComponentName == null || mComponentName.getPackageName() == null) {
80             finish();
81             return;
82         }
83 
84         try {
85             ApplicationInfo applicationInfo = getPackageManager().getApplicationInfo(
86                     mComponentName.getPackageName(), 0);
87             mAppLabel = applicationInfo.loadSafeLabel(getPackageManager(),
88                     PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX,
89                     PackageItemInfo.SAFE_LABEL_FLAG_TRIM
90                             | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE);
91         } catch (PackageManager.NameNotFoundException e) {
92             LOG.e("Couldn't find app with package name for " + mComponentName, e);
93             finish();
94             return;
95         }
96 
97         if (TextUtils.isEmpty(mAppLabel)) {
98             finish();
99             return;
100         }
101 
102         Intent NLSIntent = new Intent(NotificationListenerService.SERVICE_INTERFACE);
103         int userId = getIntent().getIntExtra(EXTRA_USER_ID, UserHandle.USER_NULL);
104         List<ResolveInfo> matchedServiceList = getPackageManager().queryIntentServicesAsUser(
105                 NLSIntent, /* flags */ 0, userId);
106         boolean hasNLSIntentFilter = false;
107         for (ResolveInfo service : matchedServiceList) {
108             if (service.serviceInfo.getComponentName().equals(mComponentName)) {
109                 if (!REQUIRED_PERMISSION.equals(service.serviceInfo.permission)) {
110                     LOG.e("Service " + mComponentName + " lacks permission " + REQUIRED_PERMISSION);
111                     finish();
112                     return;
113                 }
114                 hasNLSIntentFilter = true;
115                 break;
116             }
117         }
118         if (!hasNLSIntentFilter) {
119             LOG.e("Service " + mComponentName + " lacks an intent-filter action for "
120                     + NotificationListenerService.SERVICE_INTERFACE);
121             finish();
122             return;
123         }
124 
125         ConfirmationDialogFragment confirmationDialogFragment =
126                 new ConfirmationDialogFragment.Builder(this)
127                         .setTitle(getString(R.string.notification_listener_security_warning_title,
128                                 mAppLabel))
129                         .setMessage(
130                                 getString(R.string.notification_listener_security_warning_summary,
131                                         mAppLabel))
132                         .setPositiveButton(R.string.allow, mConfirmListener)
133                         .setNegativeButton(R.string.deny, mRejectListener)
134                         .build();
135 
136         // Consistent with the permission dialog
137         confirmationDialogFragment.setCancelable(false);
138         confirmationDialogFragment
139                 .show(getSupportFragmentManager(), ConfirmationDialogFragment.TAG);
140     }
141 
142     @Nullable
findConfirmationDialogFragment()143     private ConfirmationDialogFragment findConfirmationDialogFragment() {
144         Fragment fragment = getSupportFragmentManager().findFragmentByTag(
145                 ConfirmationDialogFragment.TAG);
146         if (fragment != null) {
147             return (ConfirmationDialogFragment) fragment;
148         }
149         return null;
150     }
151 
onAllow()152     private void onAllow() {
153         getSystemService(NotificationManager.class)
154                 .setNotificationListenerAccessGranted(mComponentName, true);
155         finish();
156     }
157 
158     @Override
dispatchPopulateAccessibilityEvent(AccessibilityEvent event)159     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
160         return AlertActivity.dispatchPopulateAccessibilityEvent(this, event);
161     }
162 
163     @Override
onBackPressed()164     public void onBackPressed() {
165         // Suppress finishing the activity on back button press,
166         // consistently with the permission dialog behavior
167     }
168 
cancel()169     private void cancel() {
170         finish();
171     }
172 }
173