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