Introduce OplusPen

Change-Id: Icfc9d2d8172aa01bf1c71a5a23a8e6387660a746
This commit is contained in:
LuK1337
2025-06-25 23:56:08 +02:00
committed by Bruno Martins
parent d58d8728aa
commit d4cfb60cf7
10 changed files with 277 additions and 0 deletions

29
Pen/Android.bp Normal file
View File

@@ -0,0 +1,29 @@
//
// SPDX-FileCopyrightText: 2025 The LineageOS Project
// SPDX-License-Identifier: Apache-2.0
//
android_app {
name: "OplusPen",
srcs: ["src/**/*.kt"],
certificate: "platform",
platform_apis: true,
system_ext_specific: true,
optimize: {
proguard_flags_files: ["proguard.flags"],
},
required: [
"default-permissions_org.lineageos.pen",
],
}
prebuilt_etc {
name: "default-permissions_org.lineageos.pen",
system_ext_specific: true,
src: "default-permissions_org.lineageos.pen.xml",
sub_dir: "default-permissions",
filename_from_src: true,
}

31
Pen/AndroidManifest.xml Normal file
View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
SPDX-FileCopyrightText: 2025 The LineageOS Project
SPDX-License-Identifier: Apache-2.0
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.lineageos.pen">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:label="@string/app_name"
android:persistent="true"
android:usesNonSdkApi="true">
<receiver
android:exported="true"
android:name=".BootCompletedReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<service
android:exported="false"
android:name=".PenService" />
</application>
</manifest>

View File

@@ -0,0 +1,13 @@
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<!--
SPDX-FileCopyrightText: 2025 The LineageOS Project
SPDX-License-Identifier: Apache-2.0
-->
<exceptions>
<exception package="org.lineageos.pen">
<permission name="android.permission.ACCESS_FINE_LOCATION" fixed="false" />
<permission name="android.permission.BLUETOOTH_CONNECT" fixed="false" />
<permission name="android.permission.BLUETOOTH_SCAN" fixed="false" />
<permission name="android.permission.POST_NOTIFICATIONS" fixed="false" />
</exception>
</exceptions>

3
Pen/proguard.flags Normal file
View File

@@ -0,0 +1,3 @@
-keep class org.lineageos.pen.* {
*;
}

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
SPDX-FileCopyrightText: Material Design Authors / Google LLC
SPDX-License-Identifier: Apache-2.0
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#000000"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/white"
android:pathData="M167,840Q146,845 130.5,829.5Q115,814 120,793L160,602L358,800L167,840ZM358,800L160,602L618,144Q641,121 675,121Q709,121 732,144L816,228Q839,251 839,285Q839,319 816,342L358,800ZM675,200L261,614L346,699L760,285Q760,285 760,285Q760,285 760,285L675,200Q675,200 675,200Q675,200 675,200Z" />
</vector>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
SPDX-FileCopyrightText: 2025 The LineageOS Project
SPDX-License-Identifier: Apache-2.0
-->
<resources>
<string name="app_name" translatable="false">OplusPen</string>
<string name="pen_attached">Pen attached</string>
<string name="tap_to_connect">Tap to connect</string>
</resources>

View File

@@ -0,0 +1,22 @@
/*
* SPDX-FileCopyrightText: 2025 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/
package org.lineageos.pen
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
class BootCompletedReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d(TAG, "Received boot completed intent")
context.startService(Intent(context, PenService::class.java))
}
companion object {
private const val TAG = "OplusPenBootReceiver"
}
}

View File

@@ -0,0 +1,145 @@
/*
* SPDX-FileCopyrightText: 2025 The LineageOS Project
* SPDX-License-Identifier: Apache-2.0
*/
package org.lineageos.pen
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.bluetooth.BluetoothManager
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanFilter
import android.bluetooth.le.ScanResult
import android.bluetooth.le.ScanSettings
import android.content.Context
import android.content.Intent
import android.os.IBinder
import android.os.UEventObserver
import android.util.Log
class PenService : Service() {
private val bluetoothManager by lazy { getSystemService(BluetoothManager::class.java) }
private val notificationManager by lazy { getSystemService(NotificationManager::class.java) }
private val observer = object : UEventObserver() {
private val lock = Any()
override fun onUEvent(event: UEvent) {
synchronized(lock) {
val pencilStatus = event.get("pencil_status") ?: return
val pencilAddr = event.get("pencil_addr")?.chunked(2)?.joinToString(":") {
it.uppercase()
} ?: return
when (pencilStatus) {
"0" -> notificationManager.cancel(NOTIFICATION_ID)
"1" -> postNotification(pencilAddr)
}
}
}
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
intent?.getStringExtra(EXTRA_PENCIL_ADDR)?.let {
bondBtDevice(it)
}
return START_STICKY
}
override fun onBind(intent: Intent?): IBinder? = null
override fun onCreate() {
super.onCreate()
observer.startObserving("DEVPATH=/devices/virtual/oplus_wireless/pencil")
}
override fun onDestroy() {
super.onDestroy()
observer.stopObserving()
}
private fun bondBtDevice(pencilAddr: String) {
val adapter = bluetoothManager.adapter
@Suppress("DEPRECATION") adapter.enable()
val scanner = adapter.bluetoothLeScanner
scanner.startScan(
listOf(ScanFilter.Builder().setDeviceAddress(pencilAddr).build()),
ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setReportDelay(0L)
.build(),
object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
super.onScanResult(callbackType, result)
scanner.stopScan(this)
result.device.createBond()
}
override fun onBatchScanResults(results: MutableList<ScanResult>) {
super.onBatchScanResults(results)
scanner.stopScan(this)
}
override fun onScanFailed(errorCode: Int) {
super.onScanFailed(errorCode)
scanner.stopScan(this)
}
}
)
}
private fun postNotification(pencilAddr: String) {
val adapter = bluetoothManager.adapter
if (adapter.bondedDevices.contains(adapter.getRemoteDevice(pencilAddr))) {
Log.e(TAG, "$pencilAddr already bonded, bailing out")
return
}
if (notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID) == null) {
notificationManager.createNotificationChannel(
NotificationChannel(
NOTIFICATION_CHANNEL_ID,
NOTIFICATION_CHANNEL_ID,
NotificationManager.IMPORTANCE_HIGH
)
)
}
val contentIntent = PendingIntent.getService(
this,
0,
Intent(this, PenService::class.java).apply {
putExtra(EXTRA_PENCIL_ADDR, pencilAddr)
},
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
val notification = Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_stylus)
.setContentTitle(getString(R.string.pen_attached))
.setContentText(getString(R.string.tap_to_connect))
.setContentIntent(contentIntent)
.setAutoCancel(true)
.build()
notificationManager.notify(NOTIFICATION_ID, notification)
}
companion object {
private const val TAG = "OplusPenService"
private const val EXTRA_PENCIL_ADDR = "pencil_addr"
private const val NOTIFICATION_CHANNEL_ID = "OplusPen"
private const val NOTIFICATION_ID = 1000
}
}

View File

@@ -0,0 +1,8 @@
type pen_app, domain, coredomain;
app_domain(pen_app)
bluetooth_domain(pen_app)
allow pen_app self:netlink_kobject_uevent_socket { create read setopt bind };
allow pen_app app_api_service:service_manager find;

View File

@@ -0,0 +1 @@
user=_app seinfo=platform name=org.lineageos.pen domain=pen_app type=app_data_file levelFrom=user