1 /*
<lambda>null2  * Copyright (C) 2022 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.permissioncontroller.safetylabel
18 
19 import com.android.permission.safetylabel.DataCategory as AppMetadataDataCategory
20 import com.android.permission.safetylabel.DataCategoryConstants
21 import com.android.permission.safetylabel.DataLabel as AppMetadataDataLabel
22 import com.android.permission.safetylabel.DataPurposeConstants.PURPOSE_ADVERTISING
23 import com.android.permission.safetylabel.SafetyLabel as AppMetadataSafetyLabel
24 import java.time.Instant
25 
26 /** Data class representing safety label history of installed apps. */
27 data class AppsSafetyLabelHistory(val appSafetyLabelHistories: List<AppSafetyLabelHistory>) {
28 
29     /** Data class representing the safety label history of an app. */
30     data class AppSafetyLabelHistory(
31         /** Information about the app. */
32         val appInfo: AppInfo,
33         /**
34          * A list of [SafetyLabel]s that this app has had in the past, ordered by
35          * [SafetyLabel.receivedAt].
36          *
37          * The last [SafetyLabel] in this list can be considered the last known [SafetyLabel] of the
38          * app.
39          */
40         val safetyLabelHistory: List<SafetyLabel>
41     ) {
42         init {
43             require(safetyLabelHistory.sortedBy { it.receivedAt } == safetyLabelHistory)
44             require(safetyLabelHistory.all { it.appInfo == appInfo })
45         }
46 
47         /**
48          * Returns an [AppSafetyLabelHistory] with the original history as well the provided safety
49          * label, ensuring that the maximum number of safety labels stored for this app does not
50          * exceed [maxToPersist].
51          *
52          * If the storage already has [maxToPersist] labels or more, the oldest will be discarded to
53          * make space for the newly added safety label.
54          */
55         fun withSafetyLabel(safetyLabel: SafetyLabel, maxToPersist: Int): AppSafetyLabelHistory =
56             AppSafetyLabelHistory(
57                 appInfo,
58                 safetyLabelHistory
59                     .toMutableList()
60                     .apply { add(safetyLabel) }
61                     .sortedBy { it.receivedAt }
62                     .takeLast(maxToPersist)
63             )
64     }
65 
66     /** Data class representing the information about an app. */
67     data class AppInfo(
68         val packageName: String,
69     )
70 
71     /** Data class representing an app's safety label. */
72     data class SafetyLabel(
73         /** Information about the app. */
74         val appInfo: AppInfo,
75         /** Earliest time at which the safety label was known to be accurate. */
76         val receivedAt: Instant,
77         /** Information about data use policies for an app. */
78         val dataLabel: DataLabel
79     ) {
80         /** Companion object for [SafetyLabel]. */
81         companion object {
82             /**
83              * Creates a safety label for persistence from the safety label parsed from
84              * PackageManager app metadata.
85              */
86             fun extractLocationSharingSafetyLabel(
87                 packageName: String,
88                 receivedAt: Instant,
89                 appMetadataSafetyLabel: AppMetadataSafetyLabel
90             ): SafetyLabel =
91                 SafetyLabel(
92                     AppInfo(packageName),
93                     receivedAt,
94                     DataLabel.extractLocationSharingDataLabel(appMetadataSafetyLabel.dataLabel)
95                 )
96         }
97     }
98 
99     /** Data class representing an app's data use policies. */
100     data class DataLabel(
101         /** Map of category to [DataCategory] */
102         val dataShared: Map<String, DataCategory>
103     ) {
104         /** Companion object for [DataCategory]. */
105         companion object {
106             /**
107              * Creates a data label for persistence from a data label parsed from PackageManager app
108              * metadata.
109              */
110             fun extractLocationSharingDataLabel(
111                 appMetadataDataLabel: AppMetadataDataLabel
112             ): DataLabel =
113                 DataLabel(
114                     appMetadataDataLabel.dataShared
115                         .filter { it.key == DataCategoryConstants.CATEGORY_LOCATION }
116                         .mapValues { categoryEntry ->
117                             DataCategory.fromAppMetadataDataCategory(categoryEntry.value)
118                         }
119                 )
120         }
121     }
122 
123     /** Data class representing an app's data use for a particular category of data. */
124     data class DataCategory(
125         /** Whether any data in this category has been used for Advertising. */
126         val containsAdvertisingPurpose: Boolean
127     ) {
128         /** Companion object for [DataCategory]. */
129         companion object {
130             /**
131              * Creates a data category for persistence from a data category parsed from
132              * PackageManager app metadata.
133              */
134             fun fromAppMetadataDataCategory(
135                 appMetadataDataCategory: AppMetadataDataCategory
136             ): DataCategory =
137                 DataCategory(
138                     appMetadataDataCategory.dataTypes.values.any {
139                         it.purposeSet.contains(PURPOSE_ADVERTISING)
140                     }
141                 )
142         }
143     }
144 
145     /** Data class representing a change of an app's safety label over time. */
146     data class AppSafetyLabelDiff(
147         val safetyLabelBefore: SafetyLabel,
148         val safetyLabelAfter: SafetyLabel
149     ) {
150         init {
151             require(safetyLabelBefore.appInfo == safetyLabelAfter.appInfo)
152         }
153     }
154 }
155