1 /*
2  * 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.providers.media.stableuris.dao;
18 
19 import android.provider.MediaStore.MediaColumns;
20 
21 import com.android.providers.media.util.StringUtils;
22 
23 import java.io.IOException;
24 import java.io.Serializable;
25 import java.util.Objects;
26 
27 /**
28  * DAO object representing database row of leveldb for volume database backup and recovery.
29  *
30  * Warning: Do not change/modify existing field names as it will affect deserialization of existing
31  * rows.
32  */
33 public final class BackupIdRow implements Serializable {
34 
35     private long mId;
36     private int mIsFavorite;
37     private int mIsPending;
38     private int mIsTrashed;
39     private boolean mIsDirty;
40 
41     /**
42      * This is not Owner Package name but a unique identifier to it
43      */
44     private int mOwnerPackageId;
45 
46     /**
47      * Same as {@link MediaColumns#DATE_EXPIRES}. Will be null if media row does not have
48      * expiry time set.
49      */
50     private String mDateExpires;
51 
52     /**
53      * This is required to support cloned user data.
54      */
55     private int mUserId;
56     private int mMediaType;
57 
58     /**
59      * Builder class for {@link BackupIdRow}
60      */
61     public static class Builder {
62         private long mId;
63         private int mIsFavorite;
64         private int mIsPending;
65         private int mIsTrashed;
66         private boolean mIsDirty;
67         private int mOwnerPackageId;
68         private String mDateExpires;
69         private int mUserId;
70         private int mMediaType;
71 
Builder(long id)72         Builder(long id) {
73             this.mId = id;
74         }
75 
76         /**
77          * Sets the isFavorite value
78          */
setIsFavorite(int isFavorite)79         public Builder setIsFavorite(int isFavorite) {
80             this.mIsFavorite = isFavorite;
81             return this;
82         }
83 
84         /**
85          * Sets the isPending value
86          */
setIsPending(int isPending)87         public Builder setIsPending(int isPending) {
88             this.mIsPending = isPending;
89             return this;
90         }
91 
92         /**
93          * Sets the isTrashed value
94          */
setIsTrashed(int isTrashed)95         public Builder setIsTrashed(int isTrashed) {
96             this.mIsTrashed = isTrashed;
97             return this;
98         }
99 
100         /**
101          * Sets the ownerPackagedId value
102          */
setOwnerPackagedId(int ownerPackagedId)103         public Builder setOwnerPackagedId(int ownerPackagedId) {
104             this.mOwnerPackageId = ownerPackagedId;
105             return this;
106         }
107 
108         /**
109          * Sets the dateExpires value
110          */
setDateExpires(String dateExpires)111         public Builder setDateExpires(String dateExpires) {
112             if (StringUtils.isNullOrEmpty(dateExpires)) {
113                 this.mDateExpires = null;
114             } else {
115                 long value = Long.parseLong(dateExpires);
116                 this.mDateExpires = String.valueOf(value);
117             }
118 
119             return this;
120         }
121 
122         /**
123          * Sets the userId value
124          */
setUserId(int userId)125         public Builder setUserId(int userId) {
126             this.mUserId = userId;
127             return this;
128         }
129 
130         /**
131          * Sets the mediatype value
132          */
setMediaType(int mediaType)133         public Builder setMediaType(int mediaType) {
134             this.mMediaType = mediaType;
135             return this;
136         }
137 
138         /**
139          * Sets the isDirty value
140          */
setIsDirty(boolean isDirty)141         public Builder setIsDirty(boolean isDirty) {
142             this.mIsDirty = isDirty;
143             return this;
144         }
145 
146         /**
147          * Builds {@link BackupIdRow} object with the given values set
148          */
build()149         public BackupIdRow build() {
150             BackupIdRow backupIdRow = new BackupIdRow(this.mId);
151             backupIdRow.mIsFavorite = this.mIsFavorite;
152             backupIdRow.mIsPending = this.mIsPending;
153             backupIdRow.mIsTrashed = this.mIsTrashed;
154             backupIdRow.mIsDirty = this.mIsDirty;
155             backupIdRow.mOwnerPackageId = this.mOwnerPackageId;
156             backupIdRow.mDateExpires = this.mDateExpires;
157             backupIdRow.mUserId = this.mUserId;
158             backupIdRow.mMediaType = this.mMediaType;
159 
160             return backupIdRow;
161         }
162     }
163 
newBuilder(long id)164     public static Builder newBuilder(long id) {
165         return new BackupIdRow.Builder(id);
166     }
167 
BackupIdRow(long id)168     private BackupIdRow(long id) {
169         this.mId = id;
170     }
171 
getId()172     public long getId() {
173         return mId;
174     }
175 
getIsFavorite()176     public int getIsFavorite() {
177         return mIsFavorite;
178     }
179 
getIsPending()180     public int getIsPending() {
181         return mIsPending;
182     }
183 
getIsTrashed()184     public int getIsTrashed() {
185         return mIsTrashed;
186     }
187 
getOwnerPackageId()188     public int getOwnerPackageId() {
189         return mOwnerPackageId;
190     }
191 
getUserId()192     public int getUserId() {
193         return mUserId;
194     }
195 
getDateExpires()196     public String getDateExpires() {
197         return mDateExpires;
198     }
199 
getIsDirty()200     public boolean getIsDirty() {
201         return mIsDirty;
202     }
203 
getMediaType()204     public int getMediaType() {
205         return mMediaType;
206     }
207 
208     /**
209      * Returns human-readable form of {@link BackupIdRow} for easy debugging.
210      */
211     @Override
toString()212     public String toString() {
213         return "BackupIdRow{" +
214                 "mId=" + mId +
215                 ", mIsFavorite=" + mIsFavorite +
216                 ", mIsPending=" + mIsPending +
217                 ", mIsTrashed=" + mIsTrashed +
218                 ", mIsDirty=" + mIsDirty +
219                 ", mOwnerPackageId=" + mOwnerPackageId +
220                 ", mDateExpires=" + mDateExpires +
221                 ", mUserId=" + mUserId +
222                 ", mMediaType=" + mMediaType +
223                 '}';
224     }
225 
226     @Override
equals(Object o)227     public boolean equals(Object o) {
228         if (this == o) return true;
229         if (!(o instanceof BackupIdRow)) return false;
230         BackupIdRow that = (BackupIdRow) o;
231         return mId == that.mId && mIsFavorite == that.mIsFavorite && mIsPending == that.mIsPending
232                 && mIsTrashed == that.mIsTrashed && mIsDirty == that.mIsDirty
233                 && mOwnerPackageId == that.mOwnerPackageId && mUserId == that.mUserId
234                 && mMediaType == that.mMediaType && Objects.equals(mDateExpires,
235                 that.mDateExpires);
236     }
237 
238     @Override
hashCode()239     public int hashCode() {
240         return Objects.hash(mId, mIsFavorite, mIsPending, mIsTrashed, mIsDirty, mOwnerPackageId,
241                 mDateExpires, mUserId, mMediaType);
242     }
243 
244     /**
245      * Serializes the given {@link BackupIdRow} object to a string
246      * Format is
247      * "is_dirty::_id::is_fav::is_pending::is_trashed::media_type::user_id::owner_id::date_expires"
248      */
serialize(BackupIdRow backupIdRow)249     public static String serialize(BackupIdRow backupIdRow) throws IOException {
250         return String.format("%s::%s::%s::%s::%s::%s::%s::%s::%s",
251                 backupIdRow.getIsDirty() ? "1" : "0", backupIdRow.getId(),
252                 backupIdRow.getIsFavorite(), backupIdRow.getIsPending(), backupIdRow.getIsTrashed(),
253                 backupIdRow.getMediaType(), backupIdRow.getUserId(),
254                 backupIdRow.getOwnerPackageId(), backupIdRow.getDateExpires());
255     }
256 
257     /**
258      * Deserializes the given string to {@link BackupIdRow} object
259      */
deserialize(String s)260     public static BackupIdRow deserialize(String s) throws IOException, ClassNotFoundException {
261         if (s == null || s.isEmpty()) {
262             return null;
263         }
264 
265         String[] fields = s.split("::");
266         BackupIdRow.Builder builder = BackupIdRow.newBuilder(Long.parseLong(fields[1]));
267         builder.setIsDirty(Objects.equals(fields[0], "1"));
268         builder.setIsFavorite(Integer.parseInt(fields[2]));
269         builder.setIsPending(Integer.parseInt(fields[3]));
270         builder.setIsTrashed(Integer.parseInt(fields[4]));
271         builder.setMediaType(Integer.parseInt(fields[5]));
272         builder.setUserId(Integer.parseInt(fields[6]));
273         builder.setOwnerPackagedId(Integer.parseInt(fields[7]));
274         builder.setDateExpires(fields[8]);
275         return builder.build();
276     }
277 }
278