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 import android.util.Log; 28 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, 83 Context.MODE_WORLD_READABLE); 84 DataOutputStream dataOut = new DataOutputStream(outputStream); 85 dataOut.writeInt(getContext().getApplicationInfo().uid); 86 dataOut.close(); 87 outputStream.close(); 88 // Ensure that some file will be accessible via the same path that will be used by other app. 89 accessPublicData(); 90 91 writeToPreferences(); 92 writeToDatabase(); 93 createTrafficStatsWithTags(); 94 } 95 accessPublicData()96 private void accessPublicData() throws IOException { 97 try { 98 // construct the absolute file path to the app's public's file the same 99 // way as the appaccessdata package will. 100 String publicFilePath = String.format("/data/data/%s/files/%s", APP_WITH_DATA_PKG, 101 PUBLIC_FILE_NAME); 102 DataInputStream inputStream = new DataInputStream(new FileInputStream(publicFilePath)); 103 int otherAppUid = (int)inputStream.readInt(); 104 inputStream.close(); 105 } catch (FileNotFoundException e) { 106 fail("Was not able to access own public file: " + e); 107 } catch (SecurityException e) { 108 fail("Was not able to access own public file: " + e); 109 } 110 } 111 writeToPreferences()112 private void writeToPreferences() { 113 SharedPreferences prefs = mContext.getSharedPreferences(PREFERENCES_FILE_NAME, 0); 114 SharedPreferences.Editor editor = prefs.edit(); 115 editor.putString(PREFERENCE_KEY, PREFERENCE_VALUE); 116 editor.commit(); 117 assertEquals(PREFERENCE_VALUE, prefs.getString(PREFERENCE_KEY, null)); 118 } 119 writeToDatabase()120 private void writeToDatabase() { 121 SQLiteDatabase db = null; 122 Cursor cursor = null; 123 try { 124 db = new TestDatabaseOpenHelper(mContext).getWritableDatabase(); 125 ContentValues values = new ContentValues(1); 126 values.put(DB_COLUMN, DB_VALUE); 127 assertTrue(db.insert(DB_TABLE_NAME, null, values) != -1); 128 129 cursor = db.query(DB_TABLE_NAME, new String[] {DB_COLUMN}, 130 null, null, null, null, null); 131 assertEquals(1, cursor.getCount()); 132 } finally { 133 if (cursor != null) { 134 cursor.close(); 135 } 136 if (db != null) { 137 db.close(); 138 } 139 } 140 } 141 142 /** 143 * Check to ensure the private file created in testCreatePrivateData does not exist. 144 * Used to check that uninstall of an app deletes the app's data. 145 */ testEnsurePrivateDataNotExist()146 public void testEnsurePrivateDataNotExist() throws IOException { 147 assertFalse(getContext().getFileStreamPath(PRIVATE_FILE_NAME).exists()); 148 149 assertPreferencesDataDoesNotExist(); 150 assertDatabaseDataDoesNotExist(); 151 } 152 assertPreferencesDataDoesNotExist()153 private void assertPreferencesDataDoesNotExist() { 154 SharedPreferences prefs = mContext.getSharedPreferences(PREFERENCES_FILE_NAME, 0); 155 assertNull(prefs.getString(PREFERENCE_KEY, null)); 156 } 157 assertDatabaseDataDoesNotExist()158 private void assertDatabaseDataDoesNotExist() { 159 SQLiteDatabase db = null; 160 Cursor cursor = null; 161 try { 162 db = new TestDatabaseOpenHelper(mContext).getWritableDatabase(); 163 cursor = db.query(DB_TABLE_NAME, new String[] {DB_COLUMN}, 164 null, null, null, null, null); 165 assertEquals(0, cursor.getCount()); 166 } finally { 167 if (cursor != null) { 168 cursor.close(); 169 } 170 if (db != null) { 171 db.close(); 172 } 173 } 174 } 175 accessOwnTrafficStats()176 private void accessOwnTrafficStats() throws IOException { 177 final int ownAppUid = getContext().getApplicationInfo().uid; 178 179 boolean foundOwnDetailedStats = false; 180 try { 181 BufferedReader qtaguidReader = new BufferedReader(new FileReader("/proc/net/xt_qtaguid/stats")); 182 String line; 183 while ((line = qtaguidReader.readLine()) != null) { 184 String tokens[] = line.split(" "); 185 if (tokens.length > 3 && tokens[3].equals(String.valueOf(ownAppUid))) { 186 if (!tokens[2].equals("0x0")) { 187 foundOwnDetailedStats = true; 188 } 189 } 190 } 191 qtaguidReader.close(); 192 } catch (FileNotFoundException e) { 193 fail("Was not able to access qtaguid/stats: " + e); 194 } 195 assertTrue("Was expecting to find own traffic stats", foundOwnDetailedStats); 196 } 197 createTrafficStatsWithTags()198 private void createTrafficStatsWithTags() throws IOException { 199 200 // Transfer 1MB of data across an explicitly localhost socket. 201 final int byteCount = 1024; 202 final int packetCount = 1024; 203 204 final ServerSocket server = new ServerSocket(0); 205 new Thread("CreatePrivateDataTest.createTrafficStatsWithTags") { 206 @Override 207 public void run() { 208 try { 209 Socket socket = new Socket("localhost", server.getLocalPort()); 210 // Make sure that each write()+flush() turns into a packet: 211 // disable Nagle. 212 socket.setTcpNoDelay(true); 213 OutputStream out = socket.getOutputStream(); 214 byte[] buf = new byte[byteCount]; 215 for (int i = 0; i < packetCount; i++) { 216 TrafficStats.setThreadStatsTag(i % 10); 217 TrafficStats.tagSocket(socket); 218 out.write(buf); 219 out.flush(); 220 } 221 out.close(); 222 socket.close(); 223 } catch (IOException e) { 224 assertTrue("io exception" + e, false); 225 } 226 } 227 }.start(); 228 229 try { 230 Socket socket = server.accept(); 231 InputStream in = socket.getInputStream(); 232 byte[] buf = new byte[byteCount]; 233 int read = 0; 234 while (read < byteCount * packetCount) { 235 int n = in.read(buf); 236 assertTrue("Unexpected EOF", n > 0); 237 read += n; 238 } 239 } finally { 240 server.close(); 241 } 242 243 accessOwnTrafficStats(); 244 } 245 246 static class TestDatabaseOpenHelper extends SQLiteOpenHelper { 247 248 static final String _ID = "_id"; 249 TestDatabaseOpenHelper(Context context)250 public TestDatabaseOpenHelper(Context context) { 251 super(context, "test.db", null, 1337); 252 } 253 254 @Override onCreate(SQLiteDatabase db)255 public void onCreate(SQLiteDatabase db) { 256 db.execSQL("CREATE TABLE " + DB_TABLE_NAME + " (" 257 + _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 258 + DB_COLUMN + " TEXT);"); 259 } 260 261 @Override onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)262 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 263 db.execSQL("DROP TABLE IF EXISTS " + DB_TABLE_NAME); 264 onCreate(db); 265 } 266 } 267 } 268