文章目录
附录
本人前一阵子一直在学习文件系统方面的知识,都说好记性不如烂笔头,我决定从今天开始总结学习到的知识,和大家一起学习,一起进步。
前言
Android外部存储空间由 vold init 服务和 StorageManagerService 系统服务共同管理。外部实体存储卷的装载由 vold 处理:通过执行分阶段操作准备好媒体,然后再将其提供给应用。
从 Android 8.0 开始,MountService 类已更名为 StorageManagerService。
并且StorageManagerService与Vold的通信方式由socket方式变为binder方式,精简了部分代码,以及之前处理信息交互的CommandListener类也被取消了。
本文涉及代码路径
StorageManagerService
StorageManagerService的启动
SystemService
StorageManagerService同其他系统服务一样,也是从SystemServer启动
//在SystemServer的startOtherServices中,storageManagerservice启动。
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
...
if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
if (!"0".equals(SystemProperties.get("system_init.startmountservice"))) {
t.traceBegin("StartStorageManagerService");
try {
/*
* NotificationManagerService is dependant on StorageManagerService,
* (for media / usb notifications) so we must start StorageManagerService first.
*/
mSystemServiceManager.startService(STORAGE_MANAGER_SERVICE_CLASS);
storageManager = IStorageManager.Stub.asInterface(
ServiceManager.getService("mount"));
} catch (Throwable e) {
reportWtf("starting StorageManagerService", e);
}
t.traceEnd();
t.traceBegin("StartStorageStatsService");
try {
mSystemServiceManager.startService(STORAGE_STATS_SERVICE_CLASS);
} catch (Throwable e) {
reportWtf("starting StorageStatsService", e);
}
t.traceEnd();
}
}
...
}
NotificationManagerService依赖于StorageManagerService,所以必需先启动StroageManagerService。
StorageManagerService服务分析
由Lifecycle启动:
private static final String STORAGE_MANAGER_SERVICE_CLASS = "com.android.server.StorageManagerService$Lifecycle";
SystemManagerService通过反射构建LifeCycle并调用onStart()方法
public void onStart() {
mStorageManagerService = new StorageManagerService(getContext());
publishBinderService("mount", mStorageManagerService);
mStorageManagerService.start();
}
public StorageManagerService(Context context) {
sSelf = this;
// 前面先是读取一些属性状态,其中关于FUSE下面会稍微介绍一下
// Snapshot feature flag used for this boot
SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT, Boolean.toString(
SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, true)));
// If there is no value in the property yet (first boot after data wipe), this value may be
// incorrect until #updateFusePropFromSettings where we set the correct value and reboot if
// different*
mIsFuseEnabled = SystemProperties.getBoolean(PROP_FUSE, DEFAULT_FUSE_ENABLED);
mVoldAppDataIsolationEnabled = mIsFuseEnabled && SystemProperties.getBoolean(
ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
mContext = context;
mResolver = mContext.getContentResolver();
mCallbacks = new Callbacks(FgThread.get().getLooper());
mLockPatternUtils = new LockPatternUtils(mContext);
// 创建名为“StorageManagerService”的线程,并创建对应的Handler
HandlerThread hthread = new HandlerThread(TAG);
hthread.start();
//新建handle,多数操作经此流转
mHandler = new StorageManagerServiceHandler(hthread.getLooper());
// mObbActionHandler对应“android.io”线程
// Add OBB Action Handler to StorageManagerService thread.
mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());
mStorageSessionController = new StorageSessionController(mContext, mIsFuseEnabled);
// 启动installd服务
mInstaller = new Installer(mContext);
mInstaller.onStart();
// Initialize the last-fstrim tracking if necessary
//如果需要,初始化last-fstrim跟踪
File dataDir = Environment.getDataDirectory();
File systemDir = new File(dataDir, "system");
mLastMaintenanceFile = new File(systemDir, LAST_FSTRIM_FILE);
//判断/data/system/last-fstrim文件,不存在则创建,存在则更新最后修改时间
if (!mLastMaintenanceFile.exists()) {
// Not setting mLastMaintenance here means that we will force an
// fstrim during reboot following the OTA that installs this code.
try {
(new FileOutputStream(mLastMaintenanceFile)).close();
} catch (IOException e) {
Slog.e(TAG, "Unable to create fstrim record " + mLastMaintenanceFile.getPath());
}
} else {
mLastMaintenance = mLastMaintenanceFile.lastModified();
}
// 读取data/system/storage.xml配置
mSettingsFile = new AtomicFile(
new File(Environment.getDataSystemDirectory(), "storage.xml"), "storage-settings");
synchronized (mLock) {
readSettingsLocked();
}
LocalServices.addService(StorageManagerInternal.class, mStorageManagerInternal);
//新建vold与cryptd线程,大部分与存储有关操作都是通过这两个线程与远程socket通信完成,会在连接成功和有消息返回时候回调
mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
null);
mConnector.setDebug(true);
mConnector.setWarnIfHeld(mLock);
mConnectorThread = new Thread(mConnector, VOLD_TAG);
// Reuse parameters from first connector since they are tested and safe
mCryptConnector = new NativeDaemonConnector(this, "cryptd",
MAX_CONTAINERS * 2, CRYPTD_TAG, 25, null);
mCryptConnector.setDebug(true);
mCryptConnectorThread = new Thread(mCryptConnector, CRYPTD_TAG);
final IntentFilter userFilter = new IntentFilter();
userFilter.addAction(Intent.ACTION_USER_ADDED);
userFilter.addAction(Intent.ACTION_USER_REMOVED);
mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
synchronized (mLock) {
addInternalVolumeLocked();
}
//添到watchdog监视队列中
if (WATCHDOG_ENABLE) {
Watchdog.getInstance().addMonitor(this);
}
}
构造函数完成后立即启动vold跟cryptd线程:
private void start() {
mConnectorThread.start();
mCryptConnectorThread.start();
}
NativeDaemonConnector:启动后连接并且监听socket返回信息
@Override
public void run() {
mCallbackHandler = new Handler(mLooper, this);
while (true) {
try {
listenToSocket();
} catch (Exception e) {
loge("Error in NativeDaemonConnector: " + e);
SystemClock.sleep(5000);
}
}
}
private void listenToSocket() throws IOException {
LocalSocket socket = null;
try {
socket = new LocalSocket();
LocalSocketAddress address = determineSocketAddress();
socket.connect(address);
InputStream inputStream = socket.getInputStream();
synchronized (mDaemonLock) {
mOutputStream = socket.getOutputStream();
}
//连接上socket之后回调storagemanagerservice
mCallbacks.onDaemonConnected();
FileDescriptor[] fdList = null;
byte[] buffer = new byte[BUFFER_SIZE];
int start = 0;
while (true) {
int count = inputStream.read(buffer, start, BUFFER_SIZE - start);
if (count < 0) {
loge("got " + count + " reading with start = " + start);
break;
}
fdList = socket.getAncillaryFileDescriptors();
// Add our starting point to the count and reset the start.
count += start;
start = 0;
for (int i = 0; i < count; i++) {
if (buffer[i] == 0) {
// Note - do not log this raw message since it may contain
// sensitive data
final String rawEvent = new String(
buffer, start, i - start, StandardCharsets.UTF_8);
boolean releaseWl = false;
try {
final NativeDaemonEvent event =
NativeDaemonEvent.parseRawEvent(rawEvent, fdList);
log("RCV <- {" + event + "}");
if (event.isClassUnsolicited()) {
// TODO: migrate to sending NativeDaemonEvent instances
if (mCallbacks.onCheckHoldWakeLock(event.getCode())
&& mWakeLock != null) {
mWakeLock.acquire();
releaseWl = true;
}
Message msg = mCallbackHandler.obtainMessage(
event.getCode(), uptimeMillisInt(), 0, event.getRawEvent());
if (mCallbackHandler.sendMessage(msg)) {
releaseWl = false;
}
} else {
mResponseQueue.add(event.getCmdNumber(), event);
}
} catch (IllegalArgumentException e) {
log("Problem parsing message " + e);
} finally {
if (releaseWl) {
mWakeLock.release();
}
}
start = i + 1;
}
}
if (start == 0) {
log("RCV incomplete");
}
// We should end at the amount we read. If not, compact then
// buffer and read again.
if (start != count) {
final int remaining = BUFFER_SIZE - start;
System.arraycopy(buffer, start, buffer, 0, remaining);
start = remaining;
} else {
start = 0;
}
}
} catch (IOException ex) {
loge("Communications error: " + ex);
throw ex;
} finally {
synchronized (mDaemonLock) {
if (mOutputStream != null) {
try {
loge("closing stream for " + mSocket);
mOutputStream.close();
} catch (IOException e) {
loge("Failed closing output stream: " + e);
}
mOutputStream = null;
}
}
try {
if (socket != null) {
socket.close();
}
} catch (IOException ex) {
loge("Failed closing socket: " + ex);
}
}
}
与vold跟crypted通信返回的数据格式都是使用的:NativeDaemonEvent,通过将从数据流中获取到的string资源解析成NativeDaemonEvent对象。
private NativeDaemonEvent(int cmdNumber, int code, String message,
String rawEvent, String logMessage, FileDescriptor[] fdList) {
mCmdNumber = cmdNumber;
mCode = code;
mMessage = message;
mRawEvent = rawEvent;
mLogMessage = logMessage;
mParsed = null;
mFdList = fdList;
}
public static NativeDaemonEvent parseRawEvent(String rawEvent, FileDescriptor[] fdList) {
final String[] parsed = rawEvent.split(" ");
if (parsed.length < 2) {
throw new IllegalArgumentException("Insufficient arguments");
}
int skiplength = 0;
final int code;
try {
code = Integer.parseInt(parsed[0]);
skiplength = parsed[0].length() + 1;
} catch (NumberFormatException e) {
throw new IllegalArgumentException("problem parsing code", e);
}
int cmdNumber = -1;
if (isClassUnsolicited(code) == false) {
if (parsed.length < 3) {
throw new IllegalArgumentException("Insufficient arguemnts");
}
try {
cmdNumber = Integer.parseInt(parsed[1]);
skiplength += parsed[1].length() + 1;
} catch (NumberFormatException e) {
throw new IllegalArgumentException("problem parsing cmdNumber", e);
}
}
String logMessage = rawEvent;
if (parsed.length > 2 && parsed[2].equals(SENSITIVE_MARKER)) {
skiplength += parsed[2].length() + 1;
logMessage = parsed[0] + " " + parsed[1] + " {}";
}
final String message = rawEvent.substring(skiplength);
return new NativeDaemonEvent(cmdNumber, code, message, rawEvent, logMessage, fdList);
}
unmount流程framework层流程分析:
@Override
public void unmount(String volId) {
enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
waitForReady();
//查找卷信息
final VolumeInfo vol = findVolumeByIdOrThrow(volId);
//通知pms卷设备变化信息
if (vol.isPrimaryPhysical()) {
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mUnmountLock) {
mUnmountSignal = new CountDownLatch(1);
mPms.updateExternalMediaStatus(false, true);
waitForLatch(mUnmountSignal, "mUnmountSignal");
mUnmountSignal = null;
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
try {
//连接socket执行unmount命令
mConnector.execute("volume", "unmount", vol.id);
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
}
public NativeDaemonEvent[] executeForList(long timeoutMs, String cmd, Object... args)
throws NativeDaemonConnectorException {
if (mWarnIfHeld != null && Thread.holdsLock(mWarnIfHeld)) {
Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding 0x"
+ Integer.toHexString(System.identityHashCode(mWarnIfHeld)), new Throwable());
}
final long startTime = SystemClock.elapsedRealtime();
final ArrayList<NativeDaemonEvent> events = Lists.newArrayList();
final StringBuilder rawBuilder = new StringBuilder();
final StringBuilder logBuilder = new StringBuilder();
final int sequenceNumber = mSequenceNumber.incrementAndGet();
makeCommand(rawBuilder, logBuilder, sequenceNumber, cmd, args);
final String rawCmd = rawBuilder.toString();
final String logCmd = logBuilder.toString();
log("SND -> {" + logCmd + "}");
synchronized (mDaemonLock) {
if (mOutputStream == null) {
throw new NativeDaemonConnectorException("missing output stream");
} else {
try {
//将命令写入到socket流中 mOutputStream.write(rawCmd.getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
throw new NativeDaemonConnectorException("problem sending command", e);
}
}
}
......