blob: a7c1bcc40d0fd42cf24e54d8c4700d17167386f8 [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.ondevicepersonalization.services.policyengine
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Before
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.junit.Test
import android.util.Log
import android.adservices.ondevicepersonalization.AppInfo
import android.adservices.ondevicepersonalization.AppUsageStatus
import android.adservices.ondevicepersonalization.DeviceMetrics
import android.adservices.ondevicepersonalization.OSVersion
import android.adservices.ondevicepersonalization.Location
import android.adservices.ondevicepersonalization.LocationStatus
import android.adservices.ondevicepersonalization.UserData
import android.os.Parcel
import android.util.ArrayMap
import com.android.libraries.pcc.chronicle.util.MutableTypedMap
import com.android.libraries.pcc.chronicle.util.TypedMap
import com.android.libraries.pcc.chronicle.api.ConnectionRequest
import com.android.libraries.pcc.chronicle.api.ConnectionResult
import com.android.libraries.pcc.chronicle.api.ReadConnection
import com.android.libraries.pcc.chronicle.api.error.ChronicleError
import com.android.libraries.pcc.chronicle.api.error.PolicyNotFound
import com.android.libraries.pcc.chronicle.api.error.PolicyViolation
import com.android.libraries.pcc.chronicle.api.error.Disabled
import com.android.libraries.pcc.chronicle.api.ProcessorNode
import com.android.ondevicepersonalization.services.policyengine.api.ChronicleManager
import com.android.ondevicepersonalization.services.policyengine.data.UserDataReader
import com.android.ondevicepersonalization.services.policyengine.data.impl.UserDataConnectionProvider
import com.android.ondevicepersonalization.services.policyengine.policy.DataIngressPolicy
import com.android.ondevicepersonalization.services.policyengine.policy.rules.KidStatusEnabled
import com.android.ondevicepersonalization.services.policyengine.policy.rules.LimitedAdsTrackingEnabled
import com.android.ondevicepersonalization.services.data.user.RawUserData
import com.android.ondevicepersonalization.services.data.user.UserDataCollector
import com.google.common.truth.Truth.assertThat
import kotlin.test.fail
@RunWith(AndroidJUnit4::class)
class UserDataReaderTest : ProcessorNode {
private lateinit var policyContext: MutableTypedMap
private val userDataCollector: UserDataCollector =
UserDataCollector.getInstanceForTest(ApplicationProvider.getApplicationContext())
private val rawUserData: RawUserData = RawUserData.getInstance()
private val TAG: String = "UserDataReaderTest"
override val requiredConnectionTypes = setOf(UserDataReader::class.java)
private val chronicleManager: ChronicleManager = ChronicleManager.getInstance(
connectionProviders = setOf(UserDataConnectionProvider()),
policies = setOf(DataIngressPolicy.NPA_DATA_POLICY),
connectionContext = TypedMap()
)
@Before
fun setUp() {
policyContext = MutableTypedMap()
policyContext[KidStatusEnabled] = false
policyContext[LimitedAdsTrackingEnabled] = false
chronicleManager.chronicle.updateConnectionContext(TypedMap(policyContext))
chronicleManager.failNewConnections(false)
userDataCollector.updateUserData(rawUserData)
}
@Test
fun testUserDataConnection() {
val result: ConnectionResult<UserDataReader>? = chronicleManager.chronicle.getConnection(
ConnectionRequest(UserDataReader::class.java, this, DataIngressPolicy.NPA_DATA_POLICY)
)
assertThat(result).isNotNull()
assertThat(result).isInstanceOf(ConnectionResult.Success::class.java)
}
@Test
fun testUserDataReader() {
try {
val userDataReader: UserDataReader? = chronicleManager.chronicle.getConnectionOrThrow(
ConnectionRequest(UserDataReader::class.java, this, DataIngressPolicy.NPA_DATA_POLICY)
)
val userData: UserData? = userDataReader?.readUserData()
// Whether user data is null should not matter to policy engine
if (userData != null) {
verifyData(userData, rawUserData)
// test real-time data update
userDataCollector.getRealTimeData(rawUserData)
val updatedUserData: UserData? = userDataReader.readUserData()
if (updatedUserData != null) {
verifyData(updatedUserData, rawUserData)
}
}
} catch (e: ChronicleError) {
Log.e(TAG, "Expect success but connection failed with: ", e)
}
}
@Test
fun testFailedConnectionContext() {
policyContext[KidStatusEnabled] = true
chronicleManager.chronicle.updateConnectionContext(TypedMap(policyContext))
val result: ConnectionResult<UserDataReader>? = chronicleManager.chronicle.getConnection(
ConnectionRequest(UserDataReader::class.java, this, DataIngressPolicy.NPA_DATA_POLICY)
)
assertThat(result).isNotNull()
result?.expectFailure(PolicyViolation::class.java)
}
@Test
fun testFailNewConnection() {
chronicleManager.failNewConnections(true)
val result: ConnectionResult<UserDataReader>? = chronicleManager.chronicle.getConnection(
ConnectionRequest(UserDataReader::class.java, this, DataIngressPolicy.NPA_DATA_POLICY)
)
assertThat(result).isNotNull()
result?.expectFailure(Disabled::class.java)
}
@Test
fun testAppInstallInfo() {
var appInstallStatus1 = AppInfo.Builder()
.setInstalled(true)
.build()
var parcel = Parcel.obtain()
appInstallStatus1.writeToParcel(parcel, 0)
parcel.setDataPosition(0);
var appInstallStatus2 = AppInfo.CREATOR.createFromParcel(parcel)
assertThat(appInstallStatus1).isEqualTo(appInstallStatus2)
assertThat(appInstallStatus1.hashCode()).isEqualTo(appInstallStatus2.hashCode())
assertThat(appInstallStatus1.describeContents()).isEqualTo(0)
}
@Test
fun testAppUsageStatus() {
var appUsageStatus1 = AppUsageStatus.Builder()
.setPackageName("package")
.setTotalTimeUsedInMillis(1000)
.build()
var parcel = Parcel.obtain()
appUsageStatus1.writeToParcel(parcel, 0)
parcel.setDataPosition(0);
var appUsageStatus2 = AppUsageStatus.CREATOR.createFromParcel(parcel)
assertThat(appUsageStatus1).isEqualTo(appUsageStatus2)
assertThat(appUsageStatus1.hashCode()).isEqualTo(appUsageStatus2.hashCode())
assertThat(appUsageStatus1.describeContents()).isEqualTo(0)
}
@Test
fun testDeviceMetrics() {
var deviceMetrics1 = DeviceMetrics.Builder()
.setMake(111)
.setModel(222)
.setScreenHeights(333)
.setScreenWidth(444)
.setXdpi(0.1f)
.setYdpi(0.2f)
.setPxRatio(0.5f)
.build()
var parcel = Parcel.obtain()
deviceMetrics1.writeToParcel(parcel, 0)
parcel.setDataPosition(0);
var deviceMetrics2 = DeviceMetrics.CREATOR.createFromParcel(parcel)
assertThat(deviceMetrics1).isEqualTo(deviceMetrics2)
assertThat(deviceMetrics1.hashCode()).isEqualTo(deviceMetrics2.hashCode())
assertThat(deviceMetrics1.describeContents()).isEqualTo(0)
}
@Test
fun testLocation() {
var location1 = Location.Builder()
.setTimestampSeconds(111111)
.setLatitude(0.1)
.setLongitude(0.2)
.setLocationProvider(1)
.setPreciseLocation(true)
.build()
var parcel = Parcel.obtain()
location1.writeToParcel(parcel, 0)
parcel.setDataPosition(0);
var location2 = Location.CREATOR.createFromParcel(parcel)
assertThat(location1).isEqualTo(location2)
assertThat(location1.hashCode()).isEqualTo(location2.hashCode())
assertThat(location1.describeContents()).isEqualTo(0)
}
@Test
fun testLocationStatus() {
var locationStatus1 = LocationStatus.Builder()
.setLatitude(0.1)
.setLongitude(0.2)
.setDurationMillis(111111)
.build()
var parcel = Parcel.obtain()
locationStatus1.writeToParcel(parcel, 0)
parcel.setDataPosition(0);
var locationStatus2 = LocationStatus.CREATOR.createFromParcel(parcel)
assertThat(locationStatus1).isEqualTo(locationStatus2)
assertThat(locationStatus1.hashCode()).isEqualTo(locationStatus2.hashCode())
assertThat(locationStatus1.describeContents()).isEqualTo(0)
}
@Test
fun testOSVersion() {
var oSVersion1 = OSVersion.Builder()
.setMajor(111)
.setMinor(222)
.setMicro(333)
.build()
var parcel = Parcel.obtain()
oSVersion1.writeToParcel(parcel, 0)
parcel.setDataPosition(0);
var oSVersion2 = OSVersion.CREATOR.createFromParcel(parcel)
assertThat(oSVersion1).isEqualTo(oSVersion2)
assertThat(oSVersion1.hashCode()).isEqualTo(oSVersion2.hashCode())
assertThat(oSVersion1.describeContents()).isEqualTo(0)
}
@Test
fun testUserData() {
val appInstalledHistory: Map<String, AppInfo> = mapOf<String, AppInfo>();
val appUsageHistory: List<AppUsageStatus> = listOf();
var location = Location.Builder()
.setTimestampSeconds(111111)
.setLatitude(0.1)
.setLongitude(0.2)
.setLocationProvider(1)
.setPreciseLocation(true)
.build()
val locationHistory: List<LocationStatus> = listOf();
var userData1 = UserData.Builder()
.setTimezoneUtcOffsetMins(1)
.setOrientation(1)
.setAvailableStorageBytes(222)
.setBatteryPercentage(33)
.setCarrier("AT_T")
.setConnectionType(2)
.setNetworkConnectionSpeedKbps(666)
.setNetworkMetered(true)
.setAppInfo(appInstalledHistory)
.setAppUsageHistory(appUsageHistory)
.setCurrentLocation(location)
.setLocationHistory(locationHistory)
.build()
var parcel = Parcel.obtain()
userData1.writeToParcel(parcel, 0)
parcel.setDataPosition(0);
var userData2 = UserData.CREATOR.createFromParcel(parcel)
assertThat(userData1).isEqualTo(userData2)
assertThat(userData1.hashCode()).isEqualTo(userData2.hashCode())
assertThat(userData1.describeContents()).isEqualTo(0)
}
private fun verifyData(userData: UserData, ref: RawUserData) {
assertThat(userData.getTimezoneUtcOffsetMins()).isEqualTo(ref.utcOffset)
assertThat(userData.getOrientation()).isEqualTo(ref.orientation)
assertThat(userData.getAvailableStorageBytes()).isEqualTo(ref.availableStorageBytes)
assertThat(userData.getBatteryPercentage()).isEqualTo(ref.batteryPercentage)
assertThat(userData.getCarrier()).isEqualTo(ref.carrier.toString())
assertThat(userData.getConnectionType()).isEqualTo(ref.connectionType.ordinal)
assertThat(userData.getNetworkConnectionSpeedKbps()).isEqualTo(ref.connectionSpeedKbps)
assertThat(userData.isNetworkMetered()).isEqualTo(ref.networkMetered)
val currentLocation: Location = userData.getCurrentLocation()
assertThat(currentLocation.getTimestampSeconds()).isEqualTo(rawUserData.currentLocation.timeMillis / 1000)
assertThat(currentLocation.getLatitude()).isEqualTo(rawUserData.currentLocation.latitude)
assertThat(currentLocation.getLongitude()).isEqualTo(rawUserData.currentLocation.longitude)
assertThat(currentLocation.getLocationProvider()).isEqualTo(rawUserData.currentLocation.provider.ordinal)
assertThat(currentLocation.isPreciseLocation()).isEqualTo(rawUserData.currentLocation.isPreciseLocation)
assertThat(userData.getAppInfo().size).isEqualTo(rawUserData.appsInfo.size)
assertThat(userData.getAppUsageHistory().size).isEqualTo(rawUserData.appUsageHistory.size)
assertThat(userData.getLocationHistory().size).isEqualTo(rawUserData.locationHistory.size)
}
private fun ConnectionResult<*>.expectFailure(cls: Class<out ChronicleError>) {
when (this) {
is ConnectionResult.Success -> fail("Expected failure with $cls, but got success")
is ConnectionResult.Failure -> assertThat(error).isInstanceOf(cls)
}
}
}