1 /* 2 * Copyright (C) 2019 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 package com.android.systemui.biometrics 17 18 import android.Manifest 19 import android.app.ActivityTaskManager 20 import android.content.Context 21 import android.content.pm.PackageManager 22 import android.graphics.Bitmap 23 import android.graphics.Canvas 24 import android.graphics.Insets 25 import android.graphics.drawable.BitmapDrawable 26 import android.graphics.drawable.Drawable 27 import android.hardware.biometrics.BiometricManager.Authenticators 28 import android.hardware.biometrics.PromptInfo 29 import android.hardware.biometrics.SensorPropertiesInternal 30 import android.os.UserManager 31 import android.util.DisplayMetrics 32 import android.util.Log 33 import android.view.ViewGroup 34 import android.view.WindowInsets 35 import android.view.WindowManager 36 import android.view.WindowMetrics 37 import android.view.accessibility.AccessibilityEvent 38 import android.view.accessibility.AccessibilityManager 39 import com.android.internal.widget.LockPatternUtils 40 import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD 41 import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN 42 import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN 43 import com.android.systemui.biometrics.shared.model.PromptKind 44 import com.android.systemui.utils.windowmanager.WindowManagerUtils 45 46 object Utils { 47 private const val TAG = "SysUIBiometricUtils" 48 49 /** Base set of layout flags for fingerprint overlay widgets. */ 50 const val FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS = 51 (WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or 52 WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or 53 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or 54 WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) 55 56 @JvmStatic dpToPixelsnull57 fun dpToPixels(context: Context, dp: Float): Float { 58 val density = context.resources.displayMetrics.densityDpi.toFloat() 59 return dp * (density / DisplayMetrics.DENSITY_DEFAULT) 60 } 61 62 /** 63 * Note: Talkback 14.0 has new rate-limitation design to reduce frequency of 64 * TYPE_WINDOW_CONTENT_CHANGED events to once every 30 seconds. (context: b/281765653#comment18) 65 * Using {@link View#announceForAccessibility} instead as workaround when sending events 66 * exceeding this frequency is required. 67 */ 68 @JvmStatic notifyAccessibilityContentChangednull69 fun notifyAccessibilityContentChanged(am: AccessibilityManager, view: ViewGroup) { 70 if (!am.isEnabled) { 71 return 72 } 73 val event = AccessibilityEvent.obtain() 74 event.eventType = AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED 75 event.contentChangeTypes = AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE 76 view.sendAccessibilityEventUnchecked(event) 77 view.notifySubtreeAccessibilityStateChanged( 78 view, 79 view, 80 AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE, 81 ) 82 } 83 84 @JvmStatic isDeviceCredentialAllowednull85 fun isDeviceCredentialAllowed(promptInfo: PromptInfo): Boolean = 86 (promptInfo.authenticators and Authenticators.DEVICE_CREDENTIAL) != 0 87 88 @JvmStatic 89 fun isBiometricAllowed(promptInfo: PromptInfo): Boolean = 90 (promptInfo.authenticators and Authenticators.BIOMETRIC_WEAK) != 0 91 92 @JvmStatic 93 fun getCredentialType(utils: LockPatternUtils, userId: Int): PromptKind = 94 when (utils.getCredentialTypeForUser(userId)) { 95 CREDENTIAL_TYPE_PATTERN -> PromptKind.Pattern 96 CREDENTIAL_TYPE_PIN -> PromptKind.Pin 97 CREDENTIAL_TYPE_PASSWORD -> PromptKind.Password 98 else -> PromptKind.Password 99 } 100 101 @JvmStatic isManagedProfilenull102 fun isManagedProfile(context: Context, userId: Int): Boolean = 103 context.getSystemService(UserManager::class.java)?.isManagedProfile(userId) ?: false 104 105 @JvmStatic 106 fun <T : SensorPropertiesInternal> findFirstSensorProperties( 107 properties: List<T>?, 108 sensorIds: IntArray, 109 ): T? = properties?.firstOrNull { sensorIds.contains(it.sensorId) } 110 111 @JvmStatic isSystemnull112 fun isSystem(context: Context, clientPackage: String?): Boolean { 113 val hasPermission = 114 (context.checkCallingOrSelfPermission(Manifest.permission.USE_BIOMETRIC_INTERNAL) == 115 PackageManager.PERMISSION_GRANTED) 116 return hasPermission && "android" == clientPackage 117 } 118 119 @JvmStatic getNavbarInsetsnull120 fun getNavbarInsets(context: Context): Insets { 121 val windowManager: WindowManager = WindowManagerUtils.getWindowManager(context) 122 val windowMetrics: WindowMetrics = windowManager.maximumWindowMetrics 123 return windowMetrics.windowInsets.getInsets(WindowInsets.Type.navigationBars()) 124 } 125 126 /** Converts `drawable` to a [Bitmap]. */ 127 @JvmStatic toBitmapnull128 fun Drawable?.toBitmap(): Bitmap? { 129 if (this == null) { 130 return null 131 } 132 if (this is BitmapDrawable) { 133 return bitmap 134 } 135 val bitmap: Bitmap = 136 if (intrinsicWidth <= 0 || intrinsicHeight <= 0) { 137 Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888) 138 // Single color bitmap will be created of 1x1 pixel 139 } else { 140 Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, Bitmap.Config.ARGB_8888) 141 } 142 val canvas = Canvas(bitmap) 143 setBounds(0, 0, canvas.width, canvas.height) 144 draw(canvas) 145 return bitmap 146 } 147 148 @JvmStatic ellipsizenull149 fun String.ellipsize(cutOffLength: Int) = 150 if (length <= cutOffLength) this else replaceRange(cutOffLength, length, "...") 151 152 // LINT.IfChange 153 @JvmStatic 154 /** 155 * Checks if a client package is running in the background or it's a system app. 156 * 157 * @param clientPackage The name of the package to be checked. 158 * @param clientClassNameIfItIsConfirmDeviceCredentialActivity The class name of 159 * ConfirmDeviceCredentialActivity. 160 * @return Whether the client package is running in background 161 */ 162 fun ActivityTaskManager.isSystemAppOrInBackground( 163 context: Context, 164 clientPackage: String, 165 clientClassNameIfItIsConfirmDeviceCredentialActivity: String? 166 ): Boolean { 167 Log.v(TAG, "Checking if the authenticating is in background, clientPackage:$clientPackage") 168 val tasks = getTasks(Int.MAX_VALUE) 169 if (tasks == null || tasks.isEmpty()) { 170 Log.w(TAG, "No running tasks reported") 171 return false 172 } 173 174 val topActivity = tasks[0].topActivity 175 val isSystemApp = isSystem(context, clientPackage) 176 val topPackageEqualsToClient = topActivity!!.packageName == clientPackage 177 val isClientConfirmDeviceCredentialActivity = 178 clientClassNameIfItIsConfirmDeviceCredentialActivity != null 179 // b/339532378: If it's ConfirmDeviceCredentialActivity, we need to check further on 180 // class name. 181 return !(isSystemApp || topPackageEqualsToClient) || 182 (isClientConfirmDeviceCredentialActivity && 183 topActivity.className != clientClassNameIfItIsConfirmDeviceCredentialActivity) 184 } 185 // LINT.ThenChange(frameworks/base/services/core/java/com/android/server/biometrics/Utils.java) 186 } 187