1 /*
2 * Copyright 2024 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 * https://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.devicediagnostics.trusted
18
19 import android.Manifest
20 import android.graphics.Bitmap
21 import android.graphics.Color
22 import android.os.Bundle
23 import android.view.View
24 import android.widget.ImageView
25 import androidx.fragment.app.commit
26 import androidx.preference.Preference
27 import androidx.preference.PreferenceFragmentCompat
28 import com.android.devicediagnostics.ApplicationInterface
29 import com.android.devicediagnostics.PermissionsHelper
30 import com.android.devicediagnostics.Protos.DeviceReport
31 import com.android.devicediagnostics.Protos.TrustedDeviceInfo
32 import com.android.devicediagnostics.R
33 import com.android.devicediagnostics.bluetooth.BluetoothConnectionData
34 import com.android.devicediagnostics.bluetooth.BluetoothServer
35 import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity
36 import com.android.settingslib.widget.LayoutPreference
37 import com.google.protobuf.ByteString
38 import com.google.zxing.BarcodeFormat
39 import com.google.zxing.qrcode.QRCodeWriter
40
41 private const val BITMAP_SIZE = 512
42
displayQrCodenull43 private fun displayQrCode(connectionData: BluetoothConnectionData): Bitmap {
44 val writer = QRCodeWriter()
45 val bitMatrix =
46 writer.encode(connectionData.toString(), BarcodeFormat.QR_CODE, BITMAP_SIZE, BITMAP_SIZE)
47 val bmp = Bitmap.createBitmap(BITMAP_SIZE, BITMAP_SIZE, Bitmap.Config.RGB_565)
48 for (x in 0 until BITMAP_SIZE) for (y in 0 until BITMAP_SIZE) bmp.setPixel(
49 x,
50 y,
51 if (bitMatrix[x, y]) Color.BLACK else Color.WHITE
52 )
53 return bmp
54 }
55
56 class QrCodeDisplayFragment() : PreferenceFragmentCompat(), BluetoothServer.StartListener {
57 private var connectionData: BluetoothConnectionData? = null
58 private var state: TrustedState? = null
59 private var viewCreated: Boolean = false
60
onCreatePreferencesnull61 override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
62 setPreferencesFromResource(R.xml.preferences_trusted_qrcode, rootKey)
63
64 val app = ApplicationInterface.app
65 val challenge = app.getRandomBytes(32)
66 state = TrustedState(challenge, DeviceReport.getDefaultInstance())
67
68 val infoBuilder = TrustedDeviceInfo.newBuilder()
69 infoBuilder.setChallenge(ByteString.copyFrom(challenge))
70 val info = infoBuilder.build()
71
72 app.getBluetoothServer().start(info, this)
73 }
74
onViewCreatednull75 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
76 super.onViewCreated(view, savedInstanceState)
77
78 viewCreated = true
79
80 if (connectionData != null) {
81 onServerAvailable(connectionData!!)
82 }
83 }
84
drawQrCodenull85 private fun drawQrCode() {
86 val bmp = displayQrCode(connectionData!!)
87
88 val layoutPreference = findPreference("qr_code") as LayoutPreference?
89 val image = layoutPreference?.findViewById<ImageView>(R.id.qrCode)
90 image?.setImageBitmap(bmp)
91 }
92
onServerAvailablenull93 override fun onServerAvailable(connectionData: BluetoothConnectionData) {
94 if (activity == null) {
95 return
96 }
97
98 this.connectionData = connectionData
99
100 if (viewCreated) {
101 drawQrCode()
102 }
103 }
104
onServerStartednull105 override fun onServerStarted() {
106 if (activity == null) {
107 return
108 }
109 launchTrustedActivity(activity!!, WaitForResultActivity::class.java.name, state)
110 activity!!.finish()
111 }
112
onServerErrornull113 override fun onServerError() {
114 if (activity == null) {
115 return
116 }
117 findPreference<Preference>("Scan")!!.also {
118 it.setTitle(R.string.bluetooth_error_title)
119 it.setSummary(R.string.bluetooth_error_summary)
120 }
121 }
122 }
123
124 class QrCodeDisplayActivity : CollapsingToolbarBaseActivity() {
125 init {}
126
127 private val permissions =
128 PermissionsHelper(
129 this,
130 arrayOf(
131 Manifest.permission.BLUETOOTH_ADVERTISE,
132 Manifest.permission.BLUETOOTH_CONNECT,
133 Manifest.permission.BLUETOOTH_SCAN,
134 Manifest.permission.ACCESS_FINE_LOCATION
135 )
<lambda>null136 ) {
137 onPermissionsGranted()
138 }
139
onCreatenull140 override fun onCreate(savedInstanceState: Bundle?) {
141 super.onCreate(savedInstanceState)
142
143 setContentView(R.layout.activity_one_fragment)
144 setTitle(R.string.evaluation_mode_title)
145 }
146
onPermissionsGrantednull147 private fun onPermissionsGranted() {
148 supportFragmentManager.commit {
149 setReorderingAllowed(true)
150 add(R.id.fragment_container_view, QrCodeDisplayFragment())
151 }
152 }
153
onResumenull154 override fun onResume() {
155 super.onResume()
156 permissions.requestPermissions()
157 }
158 }
159