Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4986ebfb authored by Darrell Shi's avatar Darrell Shi
Browse files

Add user serial number to communal db schema

This change adds a new column to the communal database storing the
serial number of the user associated with each widget. It also
implements a migration that is run when a device is updated from the
previous version of database, populating the default value as -1.

Test: atest CommunalDatabaseMigrationsTest
Test: atest CommunalBackupHelperTest
Test: atest CommunalBackupUtilsTest
Test: atest CommunalWidgetDaoTest
Test: atest CommunalWidgetRepositoryImplTest
Bug: 330945203
Flag: com.android.systemui.communal_hub
Change-Id: I93c231ab9a25f580730628815e588e986ffd0102
parent 51f534c8
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -668,6 +668,7 @@ android_library {
    ],
    asset_dirs: [
        "tests/goldens",
        "schemas",
    ],
    static_libs: [
        "SystemUI-res",
@@ -707,6 +708,7 @@ android_library {
        "androidx-constraintlayout_constraintlayout",
        "androidx.exifinterface_exifinterface",
        "androidx.room_room-runtime",
        "androidx.room_room-testing",
        "androidx.room_room-ktx",
        "device_state_flags_lib",
        "kotlinx-coroutines-android",
+50 −33
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.content.ComponentName
import android.content.applicationContext
import android.graphics.Bitmap
import android.os.UserHandle
import android.os.userManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -47,9 +48,6 @@ import com.android.systemui.log.LogBuffer
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -59,11 +57,15 @@ import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.eq
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -85,6 +87,9 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
    private val packageChangeRepository = kosmos.fakePackageChangeRepository
    private val userManager = kosmos.userManager

    private val mainUser = UserHandle(0)

    private val fakeAllowlist =
        listOf(
@@ -109,6 +114,8 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {

        whenever(communalWidgetDao.getWidgets()).thenReturn(fakeWidgets)
        whenever(communalWidgetHost.appWidgetProviders).thenReturn(fakeProviders)
        whenever(userManager.getUserSerialNumber(mainUser.identifier))
            .thenReturn(testUserSerialNumber(mainUser))

        underTest =
            CommunalWidgetRepositoryImpl(
@@ -121,6 +128,7 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
                backupManager,
                backupUtils,
                packageChangeRepository,
                userManager,
            )
    }

@@ -128,7 +136,7 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
    fun communalWidgets_queryWidgetsFromDb() =
        testScope.runTest {
            val communalItemRankEntry = CommunalItemRank(uid = 1L, rank = 1)
            val communalWidgetItemEntry = CommunalWidgetItem(uid = 1L, 1, "pk_name/cls_name", 1L)
            val communalWidgetItemEntry = CommunalWidgetItem(uid = 1L, 1, "pk_name/cls_name", 1L, 0)
            fakeWidgets.value = mapOf(communalItemRankEntry to communalWidgetItemEntry)
            fakeProviders.value = mapOf(1 to providerInfoA)

@@ -154,13 +162,13 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
            fakeWidgets.value =
                mapOf(
                    CommunalItemRank(uid = 1L, rank = 1) to
                        CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L),
                        CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0),
                    CommunalItemRank(uid = 2L, rank = 2) to
                        CommunalWidgetItem(uid = 2L, 2, "pk_2/cls_2", 2L),
                        CommunalWidgetItem(uid = 2L, 2, "pk_2/cls_2", 2L, 0),
                    CommunalItemRank(uid = 3L, rank = 3) to
                        CommunalWidgetItem(uid = 3L, 3, "pk_3/cls_3", 3L),
                        CommunalWidgetItem(uid = 3L, 3, "pk_3/cls_3", 3L, 0),
                    CommunalItemRank(uid = 4L, rank = 4) to
                        CommunalWidgetItem(uid = 4L, 4, "pk_4/cls_4", 4L),
                        CommunalWidgetItem(uid = 4L, 4, "pk_4/cls_4", 4L, 0),
                )
            fakeProviders.value =
                mapOf(
@@ -192,9 +200,9 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
            fakeWidgets.value =
                mapOf(
                    CommunalItemRank(uid = 1L, rank = 1) to
                        CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L),
                        CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0),
                    CommunalItemRank(uid = 2L, rank = 2) to
                        CommunalWidgetItem(uid = 2L, 2, "pk_2/cls_2", 2L),
                        CommunalWidgetItem(uid = 2L, 2, "pk_2/cls_2", 2L, 0),
                )
            fakeProviders.value =
                mapOf(
@@ -249,7 +257,6 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
            val provider = ComponentName("pkg_name", "cls_name")
            val id = 1
            val priority = 1
            val user = UserHandle(0)
            whenever(communalWidgetHost.getAppWidgetInfo(id))
                .thenReturn(PROVIDER_INFO_REQUIRES_CONFIGURATION)
            whenever(
@@ -259,11 +266,12 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
                    )
                )
                .thenReturn(id)
            underTest.addWidget(provider, user, priority, kosmos.widgetConfiguratorSuccess)
            underTest.addWidget(provider, mainUser, priority, kosmos.widgetConfiguratorSuccess)
            runCurrent()

            verify(communalWidgetHost).allocateIdAndBindWidget(provider, user)
            verify(communalWidgetDao).addWidget(id, provider, priority)
            verify(communalWidgetHost).allocateIdAndBindWidget(provider, mainUser)
            verify(communalWidgetDao)
                .addWidget(id, provider, priority, testUserSerialNumber(mainUser))

            // Verify backup requested
            verify(backupManager).dataChanged()
@@ -275,7 +283,6 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
            val provider = ComponentName("pkg_name", "cls_name")
            val id = 1
            val priority = 1
            val user = UserHandle(0)
            whenever(communalWidgetHost.getAppWidgetInfo(id))
                .thenReturn(PROVIDER_INFO_REQUIRES_CONFIGURATION)
            whenever(
@@ -285,11 +292,12 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
                    )
                )
                .thenReturn(id)
            underTest.addWidget(provider, user, priority, kosmos.widgetConfiguratorFail)
            underTest.addWidget(provider, mainUser, priority, kosmos.widgetConfiguratorFail)
            runCurrent()

            verify(communalWidgetHost).allocateIdAndBindWidget(provider, user)
            verify(communalWidgetDao, never()).addWidget(id, provider, priority)
            verify(communalWidgetHost).allocateIdAndBindWidget(provider, mainUser)
            verify(communalWidgetDao, never())
                .addWidget(anyInt(), any<ComponentName>(), anyInt(), anyInt())
            verify(appWidgetHost).deleteAppWidgetId(id)

            // Verify backup not requested
@@ -302,7 +310,6 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
            val provider = ComponentName("pkg_name", "cls_name")
            val id = 1
            val priority = 1
            val user = UserHandle(0)
            whenever(communalWidgetHost.getAppWidgetInfo(id))
                .thenReturn(PROVIDER_INFO_REQUIRES_CONFIGURATION)
            whenever(
@@ -312,13 +319,14 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
                    )
                )
                .thenReturn(id)
            underTest.addWidget(provider, user, priority) {
            underTest.addWidget(provider, mainUser, priority) {
                throw IllegalStateException("some error")
            }
            runCurrent()

            verify(communalWidgetHost).allocateIdAndBindWidget(provider, user)
            verify(communalWidgetDao, never()).addWidget(id, provider, priority)
            verify(communalWidgetHost).allocateIdAndBindWidget(provider, mainUser)
            verify(communalWidgetDao, never())
                .addWidget(anyInt(), any<ComponentName>(), anyInt(), anyInt())
            verify(appWidgetHost).deleteAppWidgetId(id)

            // Verify backup not requested
@@ -331,7 +339,6 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
            val provider = ComponentName("pkg_name", "cls_name")
            val id = 1
            val priority = 1
            val user = UserHandle(0)
            whenever(communalWidgetHost.getAppWidgetInfo(id))
                .thenReturn(PROVIDER_INFO_CONFIGURATION_OPTIONAL)
            whenever(
@@ -341,11 +348,12 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
                    )
                )
                .thenReturn(id)
            underTest.addWidget(provider, user, priority, kosmos.widgetConfiguratorFail)
            underTest.addWidget(provider, mainUser, priority, kosmos.widgetConfiguratorFail)
            runCurrent()

            verify(communalWidgetHost).allocateIdAndBindWidget(provider, user)
            verify(communalWidgetDao).addWidget(id, provider, priority)
            verify(communalWidgetHost).allocateIdAndBindWidget(provider, mainUser)
            verify(communalWidgetDao)
                .addWidget(id, provider, priority, testUserSerialNumber(mainUser))

            // Verify backup requested
            verify(backupManager).dataChanged()
@@ -538,9 +546,9 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
            fakeWidgets.value =
                mapOf(
                    CommunalItemRank(uid = 1L, rank = 1) to
                        CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L),
                        CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0),
                    CommunalItemRank(uid = 2L, rank = 2) to
                        CommunalWidgetItem(uid = 2L, 2, "pk_2/cls_2", 2L),
                        CommunalWidgetItem(uid = 2L, 2, "pk_2/cls_2", 2L, 0),
                )

            // Widget 1 is installed
@@ -554,7 +562,7 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
                        sessionId = 1,
                        packageName = "pk_2",
                        icon = fakeIcon,
                        user = UserHandle.CURRENT,
                        user = mainUser,
                    )
                )
            )
@@ -572,7 +580,7 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
                        priority = 2,
                        packageName = "pk_2",
                        icon = fakeIcon,
                        user = UserHandle.CURRENT,
                        user = mainUser,
                    ),
                )
        }
@@ -583,7 +591,7 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
            fakeWidgets.value =
                mapOf(
                    CommunalItemRank(uid = 1L, rank = 1) to
                        CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L),
                        CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0),
                )

            // Widget 1 is pending install
@@ -594,7 +602,7 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
                        sessionId = 1,
                        packageName = "pk_1",
                        icon = fakeIcon,
                        user = UserHandle.CURRENT,
                        user = mainUser,
                    )
                )
            )
@@ -607,7 +615,7 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
                        priority = 1,
                        packageName = "pk_1",
                        icon = fakeIcon,
                        user = UserHandle.CURRENT,
                        user = mainUser,
                    ),
                )

@@ -633,6 +641,13 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
        whenever(appWidgetHost.appWidgetIds).thenReturn(ids.toIntArray())
    }

    // Commonly the user id and user serial number are the same, but for testing purposes use a
    // simple algorithm to map a user id to a different user serial number to make sure the correct
    // value is used.
    private fun testUserSerialNumber(user: UserHandle): Int {
        return user.identifier + 100
    }

    private companion object {
        val PROVIDER_INFO_REQUIRES_CONFIGURATION =
            AppWidgetProviderInfo().apply { configure = ComponentName("test.pkg", "test.cmp") }
@@ -650,11 +665,13 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
                                widgetId = 1
                                componentName = "pk_name/fake_widget_1"
                                rank = 1
                                userSerialNumber = 0
                            },
                            CommunalHubState.CommunalWidgetItem().apply {
                                widgetId = 2
                                componentName = "pk_name/fake_widget_2"
                                rank = 2
                                userSerialNumber = 0
                            },
                        )
                        .toTypedArray()
+81 −0
Original line number Diff line number Diff line
{
  "formatVersion": 1,
  "database": {
    "version": 2,
    "identityHash": "02e2da2d36e6955200edd5fb49e63c72",
    "entities": [
      {
        "tableName": "communal_widget_table",
        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `widget_id` INTEGER NOT NULL, `component_name` TEXT NOT NULL, `item_id` INTEGER NOT NULL, `user_serial_number` INTEGER NOT NULL DEFAULT -1)",
        "fields": [
          {
            "fieldPath": "uid",
            "columnName": "uid",
            "affinity": "INTEGER",
            "notNull": true
          },
          {
            "fieldPath": "widgetId",
            "columnName": "widget_id",
            "affinity": "INTEGER",
            "notNull": true
          },
          {
            "fieldPath": "componentName",
            "columnName": "component_name",
            "affinity": "TEXT",
            "notNull": true
          },
          {
            "fieldPath": "itemId",
            "columnName": "item_id",
            "affinity": "INTEGER",
            "notNull": true
          },
          {
            "fieldPath": "userSerialNumber",
            "columnName": "user_serial_number",
            "affinity": "INTEGER",
            "notNull": true,
            "defaultValue": "-1"
          }
        ],
        "primaryKey": {
          "autoGenerate": true,
          "columnNames": [
            "uid"
          ]
        }
      },
      {
        "tableName": "communal_item_rank_table",
        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `rank` INTEGER NOT NULL DEFAULT 0)",
        "fields": [
          {
            "fieldPath": "uid",
            "columnName": "uid",
            "affinity": "INTEGER",
            "notNull": true
          },
          {
            "fieldPath": "rank",
            "columnName": "rank",
            "affinity": "INTEGER",
            "notNull": true,
            "defaultValue": "0"
          }
        ],
        "primaryKey": {
          "autoGenerate": true,
          "columnNames": [
            "uid"
          ]
        }
      }
    ],
    "setupQueries": [
      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '02e2da2d36e6955200edd5fb49e63c72')"
    ]
  }
}
 No newline at end of file
+4 −2
Original line number Diff line number Diff line
@@ -43,11 +43,13 @@ class CommunalBackupUtils(
        val widgetsFromDb = runBlocking { database.communalWidgetDao().getWidgets().first() }
        val widgetsState = mutableListOf<CommunalHubState.CommunalWidgetItem>()
        widgetsFromDb.keys.forEach { rankItem ->
            val widget = widgetsFromDb[rankItem]!!
            widgetsState.add(
                CommunalHubState.CommunalWidgetItem().apply {
                    rank = rankItem.rank
                    widgetId = widgetsFromDb[rankItem]!!.widgetId
                    componentName = widgetsFromDb[rankItem]?.componentName
                    widgetId = widget.widgetId
                    componentName = widget.componentName
                    userSerialNumber = widget.userSerialNumber
                }
            )
        }
+25 −2
Original line number Diff line number Diff line
@@ -17,17 +17,21 @@
package com.android.systemui.communal.data.db

import android.content.Context
import android.util.Log
import androidx.annotation.VisibleForTesting
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import com.android.systemui.res.R

@Database(entities = [CommunalWidgetItem::class, CommunalItemRank::class], version = 1)
@Database(entities = [CommunalWidgetItem::class, CommunalItemRank::class], version = 2)
abstract class CommunalDatabase : RoomDatabase() {
    abstract fun communalWidgetDao(): CommunalWidgetDao

    companion object {
        private const val TAG = "CommunalDatabase"
        private var instance: CommunalDatabase? = null

        /**
@@ -51,7 +55,8 @@ abstract class CommunalDatabase : RoomDatabase() {
                            context.resources.getString(R.string.config_communalDatabase)
                        )
                        .also { builder ->
                            builder.fallbackToDestructiveMigration(dropAllTables = false)
                            builder.addMigrations(MIGRATION_1_2)
                            builder.fallbackToDestructiveMigration(dropAllTables = true)
                            callback?.let { callback -> builder.addCallback(callback) }
                        }
                        .build()
@@ -64,5 +69,23 @@ abstract class CommunalDatabase : RoomDatabase() {
        fun setInstance(database: CommunalDatabase) {
            instance = database
        }

        /**
         * This migration adds a user_serial_number column and sets its default value as
         * [CommunalWidgetItem.USER_SERIAL_NUMBER_UNDEFINED]. Work profile widgets added before the
         * migration still work as expected, but they would be backed up as personal.
         */
        @VisibleForTesting
        val MIGRATION_1_2 =
            object : Migration(1, 2) {
                override fun migrate(db: SupportSQLiteDatabase) {
                    Log.i(TAG, "Migrating from version 1 to 2")
                    db.execSQL(
                        "ALTER TABLE communal_widget_table " +
                            "ADD COLUMN user_serial_number INTEGER NOT NULL DEFAULT " +
                            "${CommunalWidgetItem.USER_SERIAL_NUMBER_UNDEFINED}"
                    )
                }
            }
    }
}
Loading