1bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang/* 2bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * Copyright (C) 2017 The Android Open Source Project 3bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * 4bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * Licensed under the Apache License, Version 2.0 (the "License"); 5bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * you may not use this file except in compliance with the License. 6bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * You may obtain a copy of the License at 7bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * 8bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * http://www.apache.org/licenses/LICENSE-2.0 9bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * 10bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * Unless required by applicable law or agreed to in writing, software 11bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * distributed under the License is distributed on an "AS IS" BASIS, 12bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * See the License for the specific language governing permissions and 14bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * limitations under the License. 15bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang */ 16bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wangpackage com.android.car.test.stressfs; 17bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang 18bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wangimport android.app.Service; 19bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wangimport android.app.Notification; 20bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wangimport android.app.NotificationChannel; 21bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wangimport android.content.Context; 22bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wangimport android.content.Intent; 23bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wangimport android.net.Uri; 24bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wangimport android.os.Binder; 25bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wangimport android.os.Bundle; 26bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wangimport android.os.Environment; 27bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wangimport android.os.IBinder; 28bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wangimport android.util.Log; 29bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wangimport android.R.drawable; 30bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang 31bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wangimport java.io.*; 32bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wangimport java.util.*; 33bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang 34bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang/** 35bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * Used to stress the file writing before and during shutdown to help ensure 36bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * that the filesystem shuts down at the right time, in a consistent manner, 37bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * and does not get corrupted. 38bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * 39bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * Writes to two files - one on the data partition, one on the external storage 40bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * partition - simultaneous in two separate threads; starting over after a 41bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * certain amount of data is written. 42bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * 43bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * This class is intended to be invoked from the shell. For a 64KB file 44bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * written in 1KB chunks, invoke from the host workstation: 45bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * adb install -g StressFS.apk 46bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * adb shell am start \ 47bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * -n com.android.car.test.stressfs/.WritingActivity \ 48bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * -a com.android.car.test.stressfs.START 49bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * -d "stressfs://start?block=1024\&file=65536" 50bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * 51bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * After reboot: 52bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * adb uninstall com.android.car.test.stressfs 53bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * adb shell "rm -rf /storage/emulated/0/stressfs_data*" 54bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * 55bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * On boot after running this while shutting down, fsck should flag any 56bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * corruption that it sees resulting from this running. The goal is to set the 57bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * shutdown sequence in a manner that does not corrupt so that this check can 58bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang * be avoided. 59bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang */ 60bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wangpublic class WritingService extends Service { 61bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang 62bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang private static final String TAG = "StressFS"; 63bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang 64bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang private static final String ACTION_START = "com.android.car.test.stressfs.START"; 65bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang 66bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang private static final int DEFAULT_BLOCK_SIZE = 4096; 67bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang private static final int DEFAULT_FILE_SIZE = 16 * 1024 * 1024; 68bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang 69bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang private static final String FILENAME_PREFIX = "stressfs_data_"; 70bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang 71bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang private static final int NOTIFICATION_ID = 100; 72bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang 73bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang /** Continuously writes test data to a specified file. */ 74bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang private static class WriteToDisk extends Thread { 75bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang private final String mLogTag; 76bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang private final File mFile; 77bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang private final int mFileSize; 78bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang private final byte[] mTestData; 79bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang 80bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang public WriteToDisk( 81bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang String logTag, 82bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang File file, 83bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang int fileSize, 84bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang byte[] testData) { 85bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang mLogTag = logTag; 86bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang mFile = file; 87bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang mFileSize = fileSize; 88bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang mTestData = testData; 89bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang } 90bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang 91bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang /** Writes data to a file, restarting once the maximum amount of data is reached.*/ 92bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang @Override 93bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang public void run() { 94bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang Log.d(TAG, mLogTag + " thread started"); 95bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang while (true) { 96bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang try { 97bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang FileOutputStream fos = new FileOutputStream(mFile); 98bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang // Write in chunks until the amount of data requested 99bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang // is written. 100bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang for (int j = 0; j < mFileSize; j += mTestData.length) { 101bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang fos.write(mTestData); 102bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang } 103bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang fos.close(); 104bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang } catch (FileNotFoundException e) { 105bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang Log.e(TAG, "File not found: ", e); 106bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang } catch (IOException e) { 107bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang Log.e(TAG, "IO error: ", e); 108bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang } 109bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang } 110bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang } 111bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang } 112bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang 113bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang /** Raises service priority and starts the writing threads. */ 114bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang @Override 115bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang public IBinder onBind(Intent intent) { 116bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang Notification notification = 117bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang new Notification.Builder(this, NotificationChannel.DEFAULT_CHANNEL_ID) 118bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang .setContentTitle("Stress Filesystem service running.") 119bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang .setSmallIcon(drawable.ic_menu_save) 120bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang .build(); 121bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang 122bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang // Raise priority of this service. 123bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang startForeground(NOTIFICATION_ID, notification); 124bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang 125bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang File dataPartitionFile = getFileStreamPath(FILENAME_PREFIX + UUID.randomUUID()); 126bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang File externalPartitionFile = new File( 127bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang Environment.getExternalStorageDirectory(), FILENAME_PREFIX + UUID.randomUUID()); 128bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang 129bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang Log.i(TAG, "External storage state: " + 130bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang Environment.getExternalStorageState(externalPartitionFile)); 131bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang 132bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang Uri data = intent.getData(); 133bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang if (data != null) { 134bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang Log.i(TAG, "Data: " + data.toString()); 135bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang } 136bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang int blockSize = getQueryParam(data, "block", DEFAULT_BLOCK_SIZE); 137bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang int fileSize = getQueryParam(data, "file", DEFAULT_FILE_SIZE); 138bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang Log.i(TAG, "Block Size: " + blockSize); 139bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang Log.i(TAG, "File Size: " + fileSize); 140bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang 141bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang if (fileSize % blockSize != 0) { 142bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang Log.w(TAG, "File size should be a multiple of block size."); 143bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang } 144bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang 145bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang // Populate some test data. 146bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang StringBuilder builder = new StringBuilder(blockSize); 147bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang for (int i = 0; i < builder.capacity(); i++) { 148bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang builder.append((char)(i % 26 + 'A')); 149bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang } 150bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang byte[] testData = new String(builder).getBytes(); 151bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang 152bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang // Spawn two threads - one to write to the /data partition, one to 153bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang // write to the SD card. 154bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang new WriteToDisk("data", dataPartitionFile, fileSize, testData).start(); 155bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang new WriteToDisk("external", externalPartitionFile, fileSize, testData).start(); 156bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang 157bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang // No need to return a binder interface, since there is no more 158bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang // interaction needed from the activity starting the service. 159bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang return null; 160bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang } 161bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang 162bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang /** Keeps service alive once started. */ 163bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang @Override 164bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang public int onStartCommand(Intent intent, int flags, int startId) { 165bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang return START_STICKY; 166bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang } 167bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang 168bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang /** Parses an integer query parameter from the input Uri. */ 169bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang private int getQueryParam(Uri data, String key, int defaultValue) { 170bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang if (data == null) { 171bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang return defaultValue; 172bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang } 173bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang String inValString = data.getQueryParameter(key); 174bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang if (inValString != null) { 175bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang try { 176bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang int inVal = Integer.parseInt(inValString); 177bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang if (inVal != 0) { 178bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang return inVal; 179bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang } 180bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang } catch (NumberFormatException e) { 181bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang return defaultValue; 182bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang } 183bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang } 184bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang return defaultValue; 185bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang } 186bbf73e5412c44e643a7b751f24099cd3891adfc3Wei Wang} 187