1 /*
2  * Copyright (C) 2023 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 android.health.connect.accesslog;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.SystemApi;
22 import android.health.connect.Constants;
23 import android.health.connect.datatypes.Record;
24 import android.health.connect.datatypes.RecordTypeIdentifier;
25 import android.health.connect.internal.datatypes.utils.RecordMapper;
26 import android.os.Parcel;
27 import android.os.Parcelable;
28 
29 import java.lang.annotation.Retention;
30 import java.lang.annotation.RetentionPolicy;
31 import java.time.Instant;
32 import java.util.ArrayList;
33 import java.util.List;
34 import java.util.Objects;
35 
36 /**
37  * A class to represent access log which is logged whenever a package requests a read on a record
38  * type
39  *
40  * @hide
41  */
42 @SystemApi
43 public final class AccessLog implements Parcelable {
44     private final List<Class<? extends Record>> mRecordTypesList = new ArrayList<>();
45     private final String mPackageName;
46     private final Instant mAccessTime;
47     @OperationType.OperationTypes private final int mOperationType;
48 
49     /**
50      * Creates an access logs object that can be used to get access log request for {@code
51      * packageName}
52      *
53      * @param packageName name of the package that requested an access
54      * @param recordTypes List of Record class type the was accessed
55      * @param accessTimeInMillis time when the access was requested
56      * @param operationType Type of access
57      * @hide
58      */
AccessLog( @onNull String packageName, @NonNull @RecordTypeIdentifier.RecordType List<Integer> recordTypes, long accessTimeInMillis, @OperationType.OperationTypes int operationType)59     public AccessLog(
60             @NonNull String packageName,
61             @NonNull @RecordTypeIdentifier.RecordType List<Integer> recordTypes,
62             long accessTimeInMillis,
63             @OperationType.OperationTypes int operationType) {
64         Objects.requireNonNull(packageName);
65         Objects.requireNonNull(recordTypes);
66 
67         mPackageName = packageName;
68         RecordMapper recordMapper = RecordMapper.getInstance();
69         for (@RecordTypeIdentifier.RecordType int recordType : recordTypes) {
70             mRecordTypesList.add(
71                     recordMapper.getRecordIdToExternalRecordClassMap().get(recordType));
72         }
73         mAccessTime = Instant.ofEpochMilli(accessTimeInMillis);
74         mOperationType = operationType;
75     }
76 
AccessLog(Parcel in)77     private AccessLog(Parcel in) {
78         RecordMapper recordMapper = RecordMapper.getInstance();
79         for (@RecordTypeIdentifier.RecordType int recordType : in.createIntArray()) {
80             mRecordTypesList.add(
81                     recordMapper.getRecordIdToExternalRecordClassMap().get(recordType));
82         }
83         mPackageName = in.readString();
84         mAccessTime = Instant.ofEpochMilli(in.readLong());
85         mOperationType = in.readInt();
86     }
87 
88     @NonNull
89     public static final Creator<AccessLog> CREATOR =
90             new Creator<>() {
91                 @Override
92                 public AccessLog createFromParcel(Parcel in) {
93                     return new AccessLog(in);
94                 }
95 
96                 @Override
97                 public AccessLog[] newArray(int size) {
98                     return new AccessLog[size];
99                 }
100             };
101 
102     /** Returns List of Record types that was accessed by the app */
103     @NonNull
getRecordTypes()104     public List<Class<? extends Record>> getRecordTypes() {
105         return mRecordTypesList;
106     }
107 
108     /** Returns package name of app that accessed the records */
109     @NonNull
getPackageName()110     public String getPackageName() {
111         return mPackageName;
112     }
113 
114     /** Returns the instant at which the app accessed the record */
115     @NonNull
getAccessTime()116     public Instant getAccessTime() {
117         return mAccessTime;
118     }
119 
120     /** Returns the type of operation performed by the app */
121     @OperationType.OperationTypes
getOperationType()122     public int getOperationType() {
123         return mOperationType;
124     }
125 
126     /** Identifier for Operation type. */
127     public static final class OperationType {
128 
129         /** Identifier for read operation done on user health data. */
130         public static final int OPERATION_TYPE_READ = Constants.READ;
131 
132         /** Identifier for update or insert operation done on user health data. */
133         public static final int OPERATION_TYPE_UPSERT = Constants.UPSERT;
134 
135         /** Identifier for delete operation done on user health data. */
136         public static final int OPERATION_TYPE_DELETE = Constants.DELETE;
137 
138         /** @hide */
139         @IntDef({OPERATION_TYPE_UPSERT, OPERATION_TYPE_DELETE, OPERATION_TYPE_READ})
140         @Retention(RetentionPolicy.SOURCE)
141         public @interface OperationTypes {}
142 
OperationType()143         private OperationType() {}
144     }
145 
146     @Override
describeContents()147     public int describeContents() {
148         return 0;
149     }
150 
151     /**
152      * Flatten this object in to a Parcel.
153      *
154      * @param dest The Parcel in which the object should be written.
155      * @param flags Additional flags about how the object should be written. May be 0 or {@link
156      *     #PARCELABLE_WRITE_RETURN_VALUE}.
157      */
158     @Override
writeToParcel(@onNull Parcel dest, int flags)159     public void writeToParcel(@NonNull Parcel dest, int flags) {
160         int recordTypeCount = mRecordTypesList.size();
161         RecordMapper recordMapper = RecordMapper.getInstance();
162         @RecordTypeIdentifier.RecordType int[] recordTypes = new int[recordTypeCount];
163         for (int i = 0; i < recordTypeCount; i++) {
164             recordTypes[i] = recordMapper.getRecordType(mRecordTypesList.get(i));
165         }
166         dest.writeIntArray(recordTypes);
167         dest.writeString(mPackageName);
168         dest.writeLong(mAccessTime.toEpochMilli());
169         dest.writeInt(mOperationType);
170     }
171 }
172