• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.ondevicepersonalization.services.data.user;
18 
19 import android.content.ContentValues;
20 import android.content.Context;
21 import android.database.Cursor;
22 import android.database.sqlite.SQLiteDatabase;
23 import android.database.sqlite.SQLiteException;
24 import android.util.Log;
25 
26 import com.android.internal.annotations.VisibleForTesting;
27 import com.android.ondevicepersonalization.services.data.OnDevicePersonalizationDbHelper;
28 
29 import java.util.Calendar;
30 import java.util.List;
31 
32 /** DAO for accessing to vendor data tables. */
33 public class UserDataDao {
34     private static final String TAG = "UserDataDao";
35 
36     private static UserDataDao sUserDataDao;
37     private final OnDevicePersonalizationDbHelper mDbHelper;
38     public static final int TTL_IN_MEMORY_DAYS = 30;
39 
UserDataDao(OnDevicePersonalizationDbHelper dbHelper)40     private UserDataDao(OnDevicePersonalizationDbHelper dbHelper) {
41         this.mDbHelper = dbHelper;
42     }
43 
44     /**
45      * Returns an instance of the UserDataDao given a context.
46      *
47      * @param context    The context of the application.
48      * @return Instance of UserDataDao for accessing the requested package's table.
49      */
getInstance(Context context)50     public static UserDataDao getInstance(Context context) {
51         synchronized (UserDataDao.class) {
52             if (sUserDataDao == null) {
53                 sUserDataDao = new UserDataDao(
54                     OnDevicePersonalizationDbHelper.getInstance(context));
55             }
56             return sUserDataDao;
57         }
58     }
59 
60     /**
61      * Returns an instance of the UserDataDao given a context. This is used for testing only.
62      */
63     @VisibleForTesting
getInstanceForTest(Context context)64     public static UserDataDao getInstanceForTest(Context context) {
65         synchronized (UserDataDao.class) {
66             if (sUserDataDao == null) {
67                 sUserDataDao = new UserDataDao(
68                     OnDevicePersonalizationDbHelper.getInstanceForTest(context));
69             }
70             return sUserDataDao;
71         }
72     }
73 
74     /**
75      * Inserts location history row if it doesn't already exist.
76      *
77      * @return true if the insert succeeded, false otherwise.
78      */
insertLocationHistoryData(long timeSec, String latitude, String longitude, int source, boolean isPrecise)79     public boolean insertLocationHistoryData(long timeSec, String latitude, String longitude,
80                                              int source, boolean isPrecise) {
81         try {
82             SQLiteDatabase db = mDbHelper.getWritableDatabase();
83             if (db == null) {
84                 return false;
85             }
86             ContentValues values = new ContentValues();
87             values.put(UserDataTables.LocationHistory.TIME_SEC, timeSec);
88             values.put(UserDataTables.LocationHistory.LATITUDE, latitude);
89             values.put(UserDataTables.LocationHistory.LONGITUDE, longitude);
90             values.put(UserDataTables.LocationHistory.SOURCE, source);
91             values.put(UserDataTables.LocationHistory.IS_PRECISE, isPrecise);
92             return db.insertWithOnConflict(UserDataTables.LocationHistory.TABLE_NAME, null, values,
93                     SQLiteDatabase.CONFLICT_REPLACE) != -1;
94         } catch (SQLiteException e) {
95             Log.e(TAG, "Failed to insert location history data", e);
96             return false;
97         }
98     }
99 
100     /**
101      * Inserts a single app usage history entry.
102      *
103      * @return true if the insert succeeded, false otherwise.
104      */
insertAppUsageStatsData(String packageName, long startingTimeSec, long endingTimeSec, long totalTimeUsedSec)105     public boolean insertAppUsageStatsData(String packageName, long startingTimeSec,
106                                              long endingTimeSec, long totalTimeUsedSec) {
107         try {
108             SQLiteDatabase db = mDbHelper.getWritableDatabase();
109             ContentValues values = new ContentValues();
110             values.put(UserDataTables.AppUsageHistory.PACKAGE_NAME, packageName);
111             values.put(UserDataTables.AppUsageHistory.STARTING_TIME_SEC, startingTimeSec);
112             values.put(UserDataTables.AppUsageHistory.ENDING_TIME_SEC, endingTimeSec);
113             values.put(UserDataTables.AppUsageHistory.TOTAL_TIME_USED_SEC, totalTimeUsedSec);
114             return db.insertWithOnConflict(UserDataTables.AppUsageHistory.TABLE_NAME, null, values,
115                     SQLiteDatabase.CONFLICT_REPLACE) != -1;
116         } catch (SQLiteException e) {
117             Log.e(TAG, "Failed to insert app usage history data", e);
118             return false;
119         }
120     }
121 
122      /**
123      * Batch inserts a list of [UsageStats].
124      * @return true if all insertions succeed as a transaction, false otherwise.
125      */
batchInsertAppUsageStatsData(List<AppUsageEntry> appUsageEntries)126     public boolean batchInsertAppUsageStatsData(List<AppUsageEntry> appUsageEntries) {
127         SQLiteDatabase db = mDbHelper.getWritableDatabase();
128         if (db == null) {
129             return false;
130         }
131         try {
132             db.beginTransaction();
133             for (AppUsageEntry entry : appUsageEntries) {
134                 if (!insertAppUsageStatsData(entry.packageName, entry.startTimeMillis,
135                         entry.endTimeMillis, entry.totalTimeUsedMillis)) {
136                     return false;
137                 }
138             }
139             db.setTransactionSuccessful();
140         } finally {
141             db.endTransaction();
142         }
143         return true;
144     }
145 
146     /**
147      * Read all app usage rows collected in the last x days.
148      * @return
149      */
readAppUsageInLastXDays(int dayCount)150     public Cursor readAppUsageInLastXDays(int dayCount) {
151         if (dayCount > TTL_IN_MEMORY_DAYS) {
152             Log.e(TAG, "Illegal attempt to read " + dayCount + " rows, which is more than "
153                     + TTL_IN_MEMORY_DAYS + " days");
154             return null;
155         }
156         Calendar cal = Calendar.getInstance();
157         cal.add(Calendar.DATE, -1 * dayCount);
158         final long thresholdTimeMillis = cal.getTimeInMillis();
159         try {
160             SQLiteDatabase db = mDbHelper.getReadableDatabase();
161             String[] columns = new String[]{UserDataTables.AppUsageHistory.PACKAGE_NAME,
162                     UserDataTables.AppUsageHistory.STARTING_TIME_SEC,
163                     UserDataTables.AppUsageHistory.ENDING_TIME_SEC,
164                     UserDataTables.AppUsageHistory.TOTAL_TIME_USED_SEC};
165             String selection = UserDataTables.AppUsageHistory.ENDING_TIME_SEC + " >= ?";
166             String[] selectionArgs = new String[]{String.valueOf(thresholdTimeMillis)};
167             String orderBy = UserDataTables.AppUsageHistory.ENDING_TIME_SEC;
168             return db.query(
169                     UserDataTables.AppUsageHistory.TABLE_NAME,
170                     columns,
171                     selection,
172                     selectionArgs,
173                     null,
174                     null,
175                     orderBy
176             );
177         } catch (SQLiteException e) {
178             Log.e(TAG, "Failed to read " + UserDataTables.AppUsageHistory.TABLE_NAME
179                     + " in the last " + dayCount + " days" , e);
180         }
181         return null;
182     }
183 
184     /**
185      * Return all location rows collected in the last X days.
186      * @return
187      */
readLocationInLastXDays(int dayCount)188     public Cursor readLocationInLastXDays(int dayCount) {
189         if (dayCount > TTL_IN_MEMORY_DAYS) {
190             Log.e(TAG, "Illegal attempt to read " + dayCount + " rows, which is more than "
191                     + TTL_IN_MEMORY_DAYS + " days");
192             return null;
193         }
194         Calendar cal = Calendar.getInstance();
195         cal.add(Calendar.DATE, -1 * dayCount);
196         final long thresholdTimeMillis = cal.getTimeInMillis();
197         try {
198             SQLiteDatabase db = mDbHelper.getReadableDatabase();
199             String[] columns = new String[]{UserDataTables.LocationHistory.TIME_SEC,
200                     UserDataTables.LocationHistory.LATITUDE,
201                     UserDataTables.LocationHistory.LONGITUDE,
202                     UserDataTables.LocationHistory.SOURCE,
203                     UserDataTables.LocationHistory.IS_PRECISE};
204             String selection = UserDataTables.LocationHistory.TIME_SEC + " >= ?";
205             String[] selectionArgs = new String[]{String.valueOf(thresholdTimeMillis)};
206             String orderBy = UserDataTables.LocationHistory.TIME_SEC;
207             return db.query(
208                     UserDataTables.LocationHistory.TABLE_NAME,
209                     columns,
210                     selection,
211                     selectionArgs,
212                     null,
213                     null,
214                     orderBy
215             );
216         } catch (SQLiteException e) {
217             Log.e(TAG, "Failed to read " + UserDataTables.LocationHistory.TABLE_NAME
218                     + " in the last " + dayCount + " days" , e);
219         }
220         return null;
221     }
222 
223     /**
224      * Clear all records in user data tables.
225      * @return true if succeed, false otherwise.
226      */
clearUserData()227     public boolean clearUserData() {
228         SQLiteDatabase db = mDbHelper.getWritableDatabase();
229         if (db == null) {
230             return false;
231         }
232         db.delete(UserDataTables.AppUsageHistory.TABLE_NAME, null, null);
233         db.delete(UserDataTables.LocationHistory.TABLE_NAME, null, null);
234         return true;
235     }
236 }
237