• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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.server.net.watchlist;
18 
19 import android.annotation.Nullable;
20 import android.content.ContentValues;
21 import android.content.Context;
22 import android.database.Cursor;
23 import android.database.sqlite.SQLiteDatabase;
24 import android.database.sqlite.SQLiteOpenHelper;
25 import android.os.Environment;
26 import android.util.Pair;
27 
28 import com.android.internal.util.HexDump;
29 
30 import java.io.File;
31 import java.util.ArrayList;
32 import java.util.GregorianCalendar;
33 import java.util.HashMap;
34 import java.util.HashSet;
35 import java.util.List;
36 import java.util.Set;
37 
38 /**
39  * Helper class to process watchlist read / save watchlist reports.
40  */
41 class WatchlistReportDbHelper extends SQLiteOpenHelper {
42 
43     private static final String TAG = "WatchlistReportDbHelper";
44 
45     private static final String NAME = "watchlist_report.db";
46     private static final int VERSION = 2;
47 
48     private static final int IDLE_CONNECTION_TIMEOUT_MS = 30000;
49 
50     private static class WhiteListReportContract {
51         private static final String TABLE = "records";
52         private static final String APP_DIGEST = "app_digest";
53         private static final String CNC_DOMAIN = "cnc_domain";
54         private static final String TIMESTAMP = "timestamp";
55     }
56 
57     private static final String CREATE_TABLE_MODEL = "CREATE TABLE "
58             + WhiteListReportContract.TABLE + "("
59             + WhiteListReportContract.APP_DIGEST + " BLOB,"
60             + WhiteListReportContract.CNC_DOMAIN + " TEXT,"
61             + WhiteListReportContract.TIMESTAMP + " INTEGER DEFAULT 0" + " )";
62 
63     private static final int INDEX_DIGEST = 0;
64     private static final int INDEX_CNC_DOMAIN = 1;
65     private static final int INDEX_TIMESTAMP = 2;
66 
67     private static final String[] DIGEST_DOMAIN_PROJECTION =
68             new String[] {
69                     WhiteListReportContract.APP_DIGEST,
70                     WhiteListReportContract.CNC_DOMAIN
71             };
72 
73     private static WatchlistReportDbHelper sInstance;
74 
75     /**
76      * Aggregated watchlist records.
77      */
78     public static class AggregatedResult {
79         // A list of digests that visited c&c domain or ip before.
80         final Set<String> appDigestList;
81 
82         // The c&c domain or ip visited before.
83         @Nullable final String cncDomainVisited;
84 
85         // A list of app digests and c&c domain visited.
86         final HashMap<String, String> appDigestCNCList;
87 
AggregatedResult(Set<String> appDigestList, String cncDomainVisited, HashMap<String, String> appDigestCNCList)88         public AggregatedResult(Set<String> appDigestList, String cncDomainVisited,
89                 HashMap<String, String> appDigestCNCList) {
90             this.appDigestList = appDigestList;
91             this.cncDomainVisited = cncDomainVisited;
92             this.appDigestCNCList = appDigestCNCList;
93         }
94     }
95 
getSystemWatchlistDbFile()96     static File getSystemWatchlistDbFile() {
97         return new File(Environment.getDataSystemDirectory(), NAME);
98     }
99 
WatchlistReportDbHelper(Context context)100     private WatchlistReportDbHelper(Context context) {
101         super(context, getSystemWatchlistDbFile().getAbsolutePath(), null, VERSION);
102         // Memory optimization - close idle connections after 30s of inactivity
103         setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
104     }
105 
getInstance(Context context)106     public static synchronized WatchlistReportDbHelper getInstance(Context context) {
107         if (sInstance != null) {
108             return sInstance;
109         }
110         sInstance = new WatchlistReportDbHelper(context);
111         return sInstance;
112     }
113 
114     @Override
onCreate(SQLiteDatabase db)115     public void onCreate(SQLiteDatabase db) {
116         db.execSQL(CREATE_TABLE_MODEL);
117     }
118 
119     @Override
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)120     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
121         // TODO: For now, drop older tables and recreate new ones.
122         db.execSQL("DROP TABLE IF EXISTS " + WhiteListReportContract.TABLE);
123         onCreate(db);
124     }
125 
126     /**
127      * Insert new watchlist record.
128      *
129      * @param appDigest The digest of an app.
130      * @param cncDomain C&C domain that app visited.
131      * @return True if success.
132      */
insertNewRecord(byte[] appDigest, String cncDomain, long timestamp)133     public boolean insertNewRecord(byte[] appDigest, String cncDomain,
134             long timestamp) {
135         final SQLiteDatabase db = getWritableDatabase();
136         final ContentValues values = new ContentValues();
137         values.put(WhiteListReportContract.APP_DIGEST, appDigest);
138         values.put(WhiteListReportContract.CNC_DOMAIN, cncDomain);
139         values.put(WhiteListReportContract.TIMESTAMP, timestamp);
140         return db.insert(WhiteListReportContract.TABLE, null, values) != -1;
141     }
142 
143     /**
144      * Aggregate all records in database before input timestamp, and return a
145      * rappor encoded result.
146      */
147     @Nullable
getAggregatedRecords(long untilTimestamp)148     public AggregatedResult getAggregatedRecords(long untilTimestamp) {
149         final String selectStatement = WhiteListReportContract.TIMESTAMP + " < ?";
150 
151         final SQLiteDatabase db = getReadableDatabase();
152         Cursor c = null;
153         try {
154             c = db.query(true /* distinct */,
155                     WhiteListReportContract.TABLE, DIGEST_DOMAIN_PROJECTION, selectStatement,
156                     new String[]{Long.toString(untilTimestamp)}, null, null,
157                     null, null);
158             if (c == null) {
159                 return null;
160             }
161             final HashSet<String> appDigestList = new HashSet<>();
162             final HashMap<String, String> appDigestCNCList = new HashMap<>();
163             String cncDomainVisited = null;
164             while (c.moveToNext()) {
165                 // We use hex string here as byte[] cannot be a key in HashMap.
166                 String digestHexStr = HexDump.toHexString(c.getBlob(INDEX_DIGEST));
167                 String cncDomain = c.getString(INDEX_CNC_DOMAIN);
168 
169                 appDigestList.add(digestHexStr);
170                 if (cncDomainVisited != null) {
171                     cncDomainVisited = cncDomain;
172                 }
173                 appDigestCNCList.put(digestHexStr, cncDomain);
174             }
175             return new AggregatedResult(appDigestList, cncDomainVisited, appDigestCNCList);
176         } finally {
177             if (c != null) {
178                 c.close();
179             }
180         }
181     }
182 
183     /**
184      * Remove all the records before input timestamp.
185      *
186      * @return True if success.
187      */
cleanup(long untilTimestamp)188     public boolean cleanup(long untilTimestamp) {
189         final SQLiteDatabase db = getWritableDatabase();
190         final String clause = WhiteListReportContract.TIMESTAMP + "< " + untilTimestamp;
191         return db.delete(WhiteListReportContract.TABLE, clause, null) != 0;
192     }
193 }