1 /* 2 * Copyright (C) 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 * 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.systemui.recordissue 18 19 import android.annotation.SuppressLint 20 import android.content.ComponentName 21 import android.content.Context 22 import android.content.Intent 23 import android.content.ServiceConnection 24 import android.content.pm.PackageManager 25 import android.net.Uri 26 import android.os.Bundle 27 import android.os.Handler 28 import android.os.IBinder 29 import android.os.Looper 30 import android.os.Message 31 import android.os.Messenger 32 import android.util.Log 33 import androidx.annotation.WorkerThread 34 import com.android.systemui.dagger.SysUISingleton 35 import com.android.systemui.dagger.qualifiers.Background 36 import com.android.traceur.FileSender 37 import com.android.traceur.MessageConstants 38 import com.android.traceur.TraceUtils.PresetTraceType 39 import javax.inject.Inject 40 41 private const val TAG = "TraceurMessageSender" 42 43 @SysUISingleton 44 class TraceurMessageSender @Inject constructor(@Background private val backgroundLooper: Looper) { 45 private var binder: Messenger? = null 46 private var isBound: Boolean = false 47 48 private val traceurConnection = 49 object : ServiceConnection { onServiceConnectednull50 override fun onServiceConnected(className: ComponentName, service: IBinder) { 51 binder = Messenger(service) 52 isBound = true 53 } 54 onServiceDisconnectednull55 override fun onServiceDisconnected(className: ComponentName) { 56 binder = null 57 isBound = false 58 } 59 } 60 61 @SuppressLint("WrongConstant") 62 @WorkerThread bindToTraceurnull63 fun bindToTraceur(context: Context) { 64 if (isBound) { 65 // Binding needs to happen after the phone has been unlocked. The RecordIssueTile is 66 // initialized before this happens though, so binding is placed at a later time, during 67 // normal operations that can be repeated. This check avoids calling "bindService" 2x+ 68 return 69 } 70 try { 71 val info = 72 context.packageManager.getPackageInfo( 73 MessageConstants.TRACING_APP_PACKAGE_NAME, 74 PackageManager.MATCH_SYSTEM_ONLY 75 ) 76 val intent = 77 Intent().setClassName(info.packageName, MessageConstants.TRACING_APP_ACTIVITY) 78 val flags = 79 Context.BIND_AUTO_CREATE or 80 Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE or 81 Context.BIND_WAIVE_PRIORITY 82 context.bindService(intent, traceurConnection, flags) 83 } catch (e: Exception) { 84 Log.e(TAG, "failed to bind to Traceur's service", e) 85 } 86 } 87 88 @WorkerThread unbindFromTraceurnull89 fun unbindFromTraceur(context: Context) { 90 if (isBound) { 91 context.unbindService(traceurConnection) 92 } 93 } 94 95 @WorkerThread startTracingnull96 fun startTracing(traceType: PresetTraceType) { 97 val data = 98 Bundle().apply { putSerializable(MessageConstants.INTENT_EXTRA_TRACE_TYPE, traceType) } 99 notifyTraceur(MessageConstants.START_WHAT, data) 100 } 101 stopTracingnull102 @WorkerThread fun stopTracing() = notifyTraceur(MessageConstants.STOP_WHAT) 103 104 @WorkerThread 105 fun shareTraces(context: Context, screenRecord: Uri?) { 106 val replyHandler = Messenger(TraceurMessageHandler(context, screenRecord, backgroundLooper)) 107 notifyTraceur(MessageConstants.SHARE_WHAT, replyTo = replyHandler) 108 } 109 110 @WorkerThread notifyTraceurnull111 private fun notifyTraceur(what: Int, data: Bundle = Bundle(), replyTo: Messenger? = null) { 112 try { 113 binder!!.send( 114 Message.obtain().apply { 115 this.what = what 116 this.data = data 117 this.replyTo = replyTo 118 } 119 ) 120 } catch (e: Exception) { 121 Log.e(TAG, "failed to notify Traceur", e) 122 } 123 } 124 125 private class TraceurMessageHandler( 126 private val context: Context, 127 private val screenRecord: Uri?, 128 looper: Looper, 129 ) : Handler(looper) { 130 handleMessagenull131 override fun handleMessage(msg: Message) { 132 if (MessageConstants.SHARE_WHAT == msg.what) { 133 shareTraces( 134 msg.data.getParcelable(MessageConstants.EXTRA_PERFETTO, Uri::class.java), 135 msg.data.getParcelable(MessageConstants.EXTRA_WINSCOPE, Uri::class.java) 136 ) 137 } else { 138 throw IllegalArgumentException("received unknown msg.what: " + msg.what) 139 } 140 } 141 shareTracesnull142 private fun shareTraces(perfetto: Uri?, winscope: Uri?) { 143 val uris: List<Uri> = 144 mutableListOf<Uri>().apply { 145 perfetto?.let { add(it) } 146 winscope?.let { add(it) } 147 screenRecord?.let { add(it) } 148 } 149 val fileSharingIntent = 150 FileSender.buildSendIntent(context, uris) 151 .addFlags( 152 Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT 153 ) 154 context.startActivity(fileSharingIntent) 155 } 156 } 157 } 158