1 /* 2 * Copyright (C) 2016 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.accessibility; 18 19 import android.accessibilityservice.AccessibilityServiceInfo; 20 import android.app.Activity; 21 import android.app.AlertDialog; 22 import android.app.Dialog; 23 import android.content.Context; 24 import android.content.DialogInterface; 25 import android.os.storage.StorageManager; 26 import android.text.BidiFormatter; 27 import android.view.LayoutInflater; 28 import android.view.MotionEvent; 29 import android.view.View; 30 import android.widget.ImageView; 31 import android.widget.LinearLayout; 32 import android.widget.TextView; 33 import android.widget.Toast; 34 35 import com.android.settings.R; 36 37 import java.util.List; 38 import java.util.Locale; 39 40 /** 41 * Utility class for creating the dialog that asks users for explicit permission to grant 42 * all of the requested capabilities to an accessibility service before the service is enabled 43 */ 44 public class AccessibilityServiceWarning { createCapabilitiesDialog(Activity parentActivity, AccessibilityServiceInfo info, DialogInterface.OnClickListener listener)45 public static Dialog createCapabilitiesDialog(Activity parentActivity, 46 AccessibilityServiceInfo info, DialogInterface.OnClickListener listener) { 47 final AlertDialog ad = new AlertDialog.Builder(parentActivity) 48 .setTitle(parentActivity.getString(R.string.enable_service_title, 49 getServiceName(parentActivity, info))) 50 .setView(createEnableDialogContentView(parentActivity, info)) 51 .setPositiveButton(android.R.string.ok, listener) 52 .setNegativeButton(android.R.string.cancel, listener) 53 .create(); 54 55 final View.OnTouchListener filterTouchListener = (View v, MotionEvent event) -> { 56 // Filter obscured touches by consuming them. 57 if (((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) 58 || ((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED) != 0)) { 59 if (event.getAction() == MotionEvent.ACTION_UP) { 60 Toast.makeText(v.getContext(), R.string.touch_filtered_warning, 61 Toast.LENGTH_SHORT).show(); 62 } 63 return true; 64 } 65 return false; 66 }; 67 68 ad.create(); 69 ad.getButton(AlertDialog.BUTTON_POSITIVE).setOnTouchListener(filterTouchListener); 70 ad.setCanceledOnTouchOutside(true); 71 72 return ad; 73 } 74 75 /** 76 * Return whether the device is encrypted with legacy full disk encryption. Newer devices 77 * should be using File Based Encryption. 78 * 79 * @return true if device is encrypted 80 */ isFullDiskEncrypted()81 private static boolean isFullDiskEncrypted() { 82 return StorageManager.isNonDefaultBlockEncrypted(); 83 } 84 85 /** 86 * Get a content View for a dialog to confirm that they want to enable a service. 87 * 88 * @param context A valid context 89 * @param info The info about a service 90 * @return A content view suitable for viewing 91 */ createEnableDialogContentView(Context context, AccessibilityServiceInfo info)92 private static View createEnableDialogContentView(Context context, 93 AccessibilityServiceInfo info) { 94 LayoutInflater inflater = (LayoutInflater) context.getSystemService( 95 Context.LAYOUT_INFLATER_SERVICE); 96 97 View content = inflater.inflate(R.layout.enable_accessibility_service_dialog_content, 98 null); 99 100 TextView encryptionWarningView = (TextView) content.findViewById( 101 R.id.encryption_warning); 102 if (isFullDiskEncrypted()) { 103 String text = context.getString(R.string.enable_service_encryption_warning, 104 getServiceName(context, info)); 105 encryptionWarningView.setText(text); 106 encryptionWarningView.setVisibility(View.VISIBLE); 107 } else { 108 encryptionWarningView.setVisibility(View.GONE); 109 } 110 111 TextView capabilitiesHeaderView = (TextView) content.findViewById( 112 R.id.capabilities_header); 113 capabilitiesHeaderView.setText(context.getString(R.string.capabilities_list_title, 114 getServiceName(context, info))); 115 116 LinearLayout capabilitiesView = (LinearLayout) content.findViewById(R.id.capabilities); 117 118 // This capability is implicit for all services. 119 View capabilityView = inflater.inflate( 120 com.android.internal.R.layout.app_permission_item_old, null); 121 122 ImageView imageView = (ImageView) capabilityView.findViewById( 123 com.android.internal.R.id.perm_icon); 124 imageView.setImageDrawable(context.getDrawable( 125 com.android.internal.R.drawable.ic_text_dot)); 126 127 TextView labelView = (TextView) capabilityView.findViewById( 128 com.android.internal.R.id.permission_group); 129 labelView.setText(context.getString( 130 R.string.capability_title_receiveAccessibilityEvents)); 131 132 TextView descriptionView = (TextView) capabilityView.findViewById( 133 com.android.internal.R.id.permission_list); 134 descriptionView.setText( 135 context.getString(R.string.capability_desc_receiveAccessibilityEvents)); 136 137 List<AccessibilityServiceInfo.CapabilityInfo> capabilities = 138 info.getCapabilityInfos(context); 139 140 capabilitiesView.addView(capabilityView); 141 142 // Service-specific capabilities. 143 final int capabilityCount = capabilities.size(); 144 for (int i = 0; i < capabilityCount; i++) { 145 AccessibilityServiceInfo.CapabilityInfo capability = capabilities.get(i); 146 147 capabilityView = inflater.inflate( 148 com.android.internal.R.layout.app_permission_item_old, null); 149 150 imageView = (ImageView) capabilityView.findViewById( 151 com.android.internal.R.id.perm_icon); 152 imageView.setImageDrawable(context.getDrawable( 153 com.android.internal.R.drawable.ic_text_dot)); 154 155 labelView = (TextView) capabilityView.findViewById( 156 com.android.internal.R.id.permission_group); 157 labelView.setText(context.getString(capability.titleResId)); 158 159 descriptionView = (TextView) capabilityView.findViewById( 160 com.android.internal.R.id.permission_list); 161 descriptionView.setText(context.getString(capability.descResId)); 162 163 capabilitiesView.addView(capabilityView); 164 } 165 166 return content; 167 } 168 169 // Get the service name and bidi wrap it to protect from bidi side effects. getServiceName(Context context, AccessibilityServiceInfo info)170 private static CharSequence getServiceName(Context context, AccessibilityServiceInfo info) { 171 final Locale locale = context.getResources().getConfiguration().getLocales().get(0); 172 final CharSequence label = 173 info.getResolveInfo().loadLabel(context.getPackageManager()); 174 return BidiFormatter.getInstance(locale).unicodeWrap(label); 175 } 176 } 177