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