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.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
20 import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
21 import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX
22 import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_MANAGED
23 import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
24 import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX
25 import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
26 import android.content.Context
27 import android.content.pm.PackageManager
28 import android.graphics.Bitmap
29 import android.graphics.Canvas
30 import android.graphics.Insets
31 import android.graphics.drawable.BitmapDrawable
32 import android.graphics.drawable.Drawable
33 import android.hardware.biometrics.BiometricManager.Authenticators
34 import android.hardware.biometrics.PromptInfo
35 import android.hardware.biometrics.SensorPropertiesInternal
36 import android.os.UserManager
37 import android.util.DisplayMetrics
38 import android.view.ViewGroup
39 import android.view.WindowInsets
40 import android.view.WindowManager
41 import android.view.WindowMetrics
42 import android.view.accessibility.AccessibilityEvent
43 import android.view.accessibility.AccessibilityManager
44 import com.android.internal.widget.LockPatternUtils
45 import com.android.systemui.biometrics.shared.model.PromptKind
46 
47 object Utils {
48     /** Base set of layout flags for fingerprint overlay widgets. */
49     const val FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS =
50         (WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or
51             WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
52             WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
53             WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)
54 
55     @JvmStatic
dpToPixelsnull56     fun dpToPixels(context: Context, dp: Float): Float {
57         val density = context.resources.displayMetrics.densityDpi.toFloat()
58         return dp * (density / DisplayMetrics.DENSITY_DEFAULT)
59     }
60 
61     /**
62      * Note: Talkback 14.0 has new rate-limitation design to reduce frequency of
63      * TYPE_WINDOW_CONTENT_CHANGED events to once every 30 seconds. (context: b/281765653#comment18)
64      * Using {@link View#announceForAccessibility} instead as workaround when sending events
65      * exceeding this frequency is required.
66      */
67     @JvmStatic
notifyAccessibilityContentChangednull68     fun notifyAccessibilityContentChanged(am: AccessibilityManager, view: ViewGroup) {
69         if (!am.isEnabled) {
70             return
71         }
72         val event = AccessibilityEvent.obtain()
73         event.eventType = AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
74         event.contentChangeTypes = AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE
75         view.sendAccessibilityEventUnchecked(event)
76         view.notifySubtreeAccessibilityStateChanged(
77             view,
78             view,
79             AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE
80         )
81     }
82 
83     @JvmStatic
isDeviceCredentialAllowednull84     fun isDeviceCredentialAllowed(promptInfo: PromptInfo): Boolean =
85         (promptInfo.authenticators and Authenticators.DEVICE_CREDENTIAL) != 0
86 
87     @JvmStatic
88     fun isBiometricAllowed(promptInfo: PromptInfo): Boolean =
89         (promptInfo.authenticators and Authenticators.BIOMETRIC_WEAK) != 0
90 
91     @JvmStatic
92     fun getCredentialType(utils: LockPatternUtils, userId: Int): PromptKind =
93         when (utils.getKeyguardStoredPasswordQuality(userId)) {
94             PASSWORD_QUALITY_SOMETHING -> PromptKind.Pattern
95             PASSWORD_QUALITY_NUMERIC,
96             PASSWORD_QUALITY_NUMERIC_COMPLEX -> PromptKind.Pin
97             PASSWORD_QUALITY_ALPHABETIC,
98             PASSWORD_QUALITY_ALPHANUMERIC,
99             PASSWORD_QUALITY_COMPLEX,
100             PASSWORD_QUALITY_MANAGED -> PromptKind.Password
101             else -> PromptKind.Password
102         }
103 
104     @JvmStatic
isManagedProfilenull105     fun isManagedProfile(context: Context, userId: Int): Boolean =
106         context.getSystemService(UserManager::class.java)?.isManagedProfile(userId) ?: false
107 
108     @JvmStatic
109     fun <T : SensorPropertiesInternal> findFirstSensorProperties(
110         properties: List<T>?,
111         sensorIds: IntArray
112     ): T? = properties?.firstOrNull { sensorIds.contains(it.sensorId) }
113 
114     @JvmStatic
isSystemnull115     fun isSystem(context: Context, clientPackage: String?): Boolean {
116         val hasPermission =
117             (context.checkCallingOrSelfPermission(Manifest.permission.USE_BIOMETRIC_INTERNAL) ==
118                 PackageManager.PERMISSION_GRANTED)
119         return hasPermission && "android" == clientPackage
120     }
121 
122     @JvmStatic
getNavbarInsetsnull123     fun getNavbarInsets(context: Context): Insets {
124         val windowManager: WindowManager? = context.getSystemService(WindowManager::class.java)
125         val windowMetrics: WindowMetrics? = windowManager?.maximumWindowMetrics
126         return windowMetrics?.windowInsets?.getInsets(WindowInsets.Type.navigationBars())
127             ?: Insets.NONE
128     }
129 
130     /** Converts `drawable` to a [Bitmap]. */
131     @JvmStatic
toBitmapnull132     fun Drawable?.toBitmap(): Bitmap? {
133         if (this == null) {
134             return null
135         }
136         if (this is BitmapDrawable) {
137             return bitmap
138         }
139         val bitmap: Bitmap =
140             if (intrinsicWidth <= 0 || intrinsicHeight <= 0) {
141                 Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
142                 // Single color bitmap will be created of 1x1 pixel
143             } else {
144                 Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, Bitmap.Config.ARGB_8888)
145             }
146         val canvas = Canvas(bitmap)
147         setBounds(0, 0, canvas.width, canvas.height)
148         draw(canvas)
149         return bitmap
150     }
151 
152     @JvmStatic
ellipsizenull153     fun String.ellipsize(cutOffLength: Int) =
154         if (length <= cutOffLength) this else replaceRange(cutOffLength, length, "...")
155 }
156