1 /* 2 * Copyright (C) 2009 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.cts.appwithdata; 18 19 import android.content.ContentValues; 20 import android.content.Context; 21 import android.content.SharedPreferences; 22 import android.database.Cursor; 23 import android.database.sqlite.SQLiteDatabase; 24 import android.database.sqlite.SQLiteOpenHelper; 25 import android.net.TrafficStats; 26 import android.test.AndroidTestCase; 27 28 import java.io.File; 29 import java.net.ServerSocket; 30 import java.net.Socket; 31 32 import java.io.BufferedReader; 33 import java.io.DataInputStream; 34 import java.io.DataOutputStream; 35 import java.io.FileInputStream; 36 import java.io.FileNotFoundException; 37 import java.io.FileOutputStream; 38 import java.io.FileReader; 39 import java.io.IOException; 40 import java.io.InputStream; 41 import java.io.OutputStream; 42 43 /** 44 * Test that will create private app data. 45 * 46 * This is not really a test per-say. Its just used as a hook so the test controller can trigger 47 * the creation of private app data. 48 */ 49 public class CreatePrivateDataTest extends AndroidTestCase { 50 51 /** 52 * The Android package name of the application that owns the private data 53 */ 54 private static final String APP_WITH_DATA_PKG = "com.android.cts.appwithdata"; 55 56 /** 57 * Name of private file to create. 58 */ 59 private static final String PRIVATE_FILE_NAME = "private_file.txt"; 60 private static final String PUBLIC_FILE_NAME = "public_file.txt"; 61 62 private static final String PREFERENCES_FILE_NAME = "preferences"; 63 private static final String PREFERENCE_KEY = "preference_key"; 64 private static final String PREFERENCE_VALUE = "preference_value"; 65 66 static final String DB_TABLE_NAME = "test_table"; 67 static final String DB_COLUMN = "test_column"; 68 static final String DB_VALUE = "test_value"; 69 70 /** 71 * Creates the private data for this app, which includes 72 * file, database entries, and traffic stats. 73 * @throws IOException if any error occurred when creating the file 74 */ testCreatePrivateData()75 public void testCreatePrivateData() throws IOException { 76 FileOutputStream outputStream = getContext().openFileOutput(PRIVATE_FILE_NAME, 77 Context.MODE_PRIVATE); 78 outputStream.write("file contents".getBytes()); 79 outputStream.close(); 80 assertTrue(getContext().getFileStreamPath(PRIVATE_FILE_NAME).exists()); 81 82 outputStream = getContext().openFileOutput(PUBLIC_FILE_NAME, 0 /*mode*/); 83 DataOutputStream dataOut = new DataOutputStream(outputStream); 84 dataOut.writeInt(getContext().getApplicationInfo().uid); 85 dataOut.close(); 86 outputStream.close(); 87 // Ensure that some file will be accessible via the same path that will be used by other app. 88 accessPublicData(); 89 90 writeToPreferences(); 91 writeToDatabase(); 92 createTrafficStatsWithTags(); 93 } 94 accessPublicData()95 private void accessPublicData() throws IOException { 96 try { 97 // construct the absolute file path to the app's public's file the same 98 // way as the appaccessdata package will. 99 File publicFile = new File(mContext.getFilesDir(), PUBLIC_FILE_NAME); 100 DataInputStream inputStream = new DataInputStream(new FileInputStream(publicFile)); 101 inputStream.readInt(); 102 inputStream.close(); 103 } catch (FileNotFoundException | SecurityException e) { 104 fail("Was not able to access own public file: " + e); 105 } 106 } 107 writeToPreferences()108 private void writeToPreferences() { 109 SharedPreferences prefs = mContext.getSharedPreferences(PREFERENCES_FILE_NAME, 0); 110 SharedPreferences.Editor editor = prefs.edit(); 111 editor.putString(PREFERENCE_KEY, PREFERENCE_VALUE); 112 editor.commit(); 113 assertEquals(PREFERENCE_VALUE, prefs.getString(PREFERENCE_KEY, null)); 114 } 115 writeToDatabase()116 private void writeToDatabase() { 117 SQLiteDatabase db = null; 118 Cursor cursor = null; 119 try { 120 db = new TestDatabaseOpenHelper(mContext).getWritableDatabase(); 121 ContentValues values = new ContentValues(1); 122 values.put(DB_COLUMN, DB_VALUE); 123 assertTrue(db.insert(DB_TABLE_NAME, null, values) != -1); 124 125 cursor = db.query(DB_TABLE_NAME, new String[] {DB_COLUMN}, 126 null, null, null, null, null); 127 assertEquals(1, cursor.getCount()); 128 } finally { 129 if (cursor != null) { 130 cursor.close(); 131 } 132 if (db != null) { 133 db.close(); 134 } 135 } 136 } 137 138 /** 139 * Check to ensure the private file created in testCreatePrivateData does not exist. 140 * Used to check that uninstall of an app deletes the app's data. 141 */ testEnsurePrivateDataNotExist()142 public void testEnsurePrivateDataNotExist() throws IOException { 143 assertFalse(getContext().getFileStreamPath(PRIVATE_FILE_NAME).exists()); 144 145 assertPreferencesDataDoesNotExist(); 146 assertDatabaseDataDoesNotExist(); 147 } 148 assertPreferencesDataDoesNotExist()149 private void assertPreferencesDataDoesNotExist() { 150 SharedPreferences prefs = mContext.getSharedPreferences(PREFERENCES_FILE_NAME, 0); 151 assertNull(prefs.getString(PREFERENCE_KEY, null)); 152 } 153 assertDatabaseDataDoesNotExist()154 private void assertDatabaseDataDoesNotExist() { 155 SQLiteDatabase db = null; 156 Cursor cursor = null; 157 try { 158 db = new TestDatabaseOpenHelper(mContext).getWritableDatabase(); 159 cursor = db.query(DB_TABLE_NAME, new String[] {DB_COLUMN}, 160 null, null, null, null, null); 161 assertEquals(0, cursor.getCount()); 162 } finally { 163 if (cursor != null) { 164 cursor.close(); 165 } 166 if (db != null) { 167 db.close(); 168 } 169 } 170 } 171 accessOwnTrafficStats()172 private void accessOwnTrafficStats() throws IOException { 173 final int ownAppUid = getContext().getApplicationInfo().uid; 174 175 boolean foundOwnDetailedStats = false; 176 try { 177 BufferedReader qtaguidReader = new BufferedReader(new FileReader("/proc/net/xt_qtaguid/stats")); 178 String line; 179 while ((line = qtaguidReader.readLine()) != null) { 180 String tokens[] = line.split(" "); 181 if (tokens.length > 3 && tokens[3].equals(String.valueOf(ownAppUid))) { 182 if (!tokens[2].equals("0x0")) { 183 foundOwnDetailedStats = true; 184 } 185 } 186 } 187 qtaguidReader.close(); 188 } catch (FileNotFoundException e) { 189 fail("Was not able to access qtaguid/stats: " + e); 190 } 191 assertTrue("Was expecting to find own traffic stats", foundOwnDetailedStats); 192 } 193 createTrafficStatsWithTags()194 private void createTrafficStatsWithTags() throws IOException { 195 196 // Transfer 1MB of data across an explicitly localhost socket. 197 final int byteCount = 1024; 198 final int packetCount = 1024; 199 200 final ServerSocket server = new ServerSocket(0); 201 new Thread("CreatePrivateDataTest.createTrafficStatsWithTags") { 202 @Override 203 public void run() { 204 try { 205 Socket socket = new Socket("localhost", server.getLocalPort()); 206 // Make sure that each write()+flush() turns into a packet: 207 // disable Nagle. 208 socket.setTcpNoDelay(true); 209 OutputStream out = socket.getOutputStream(); 210 byte[] buf = new byte[byteCount]; 211 for (int i = 0; i < packetCount; i++) { 212 TrafficStats.setThreadStatsTag(i % 10); 213 TrafficStats.tagSocket(socket); 214 out.write(buf); 215 out.flush(); 216 } 217 out.close(); 218 socket.close(); 219 } catch (IOException e) { 220 assertTrue("io exception" + e, false); 221 } 222 } 223 }.start(); 224 225 try { 226 Socket socket = server.accept(); 227 InputStream in = socket.getInputStream(); 228 byte[] buf = new byte[byteCount]; 229 int read = 0; 230 while (read < byteCount * packetCount) { 231 int n = in.read(buf); 232 assertTrue("Unexpected EOF", n > 0); 233 read += n; 234 } 235 } finally { 236 server.close(); 237 } 238 239 accessOwnTrafficStats(); 240 } 241 242 static class TestDatabaseOpenHelper extends SQLiteOpenHelper { 243 244 static final String _ID = "_id"; 245 TestDatabaseOpenHelper(Context context)246 public TestDatabaseOpenHelper(Context context) { 247 super(context, "test.db", null, 1337); 248 } 249 250 @Override onCreate(SQLiteDatabase db)251 public void onCreate(SQLiteDatabase db) { 252 db.execSQL("CREATE TABLE " + DB_TABLE_NAME + " (" 253 + _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 254 + DB_COLUMN + " TEXT);"); 255 } 256 257 @Override onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)258 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 259 db.execSQL("DROP TABLE IF EXISTS " + DB_TABLE_NAME); 260 onCreate(db); 261 } 262 } 263 } 264