最初的线索是,设置中的中文字符串“正在检查...”,通过 R.string.ext_media_status_checking 找到下面这个文件,
base/core/java/android/os/storage/VolumeInfo.java
static {
sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTED, Environment.MEDIA_UNMOUNTED);
sStateToEnvironment.put(VolumeInfo.STATE_CHECKING, Environment.MEDIA_CHECKING);
sStateToEnvironment.put(VolumeInfo.STATE_MOUNTED, Environment.MEDIA_MOUNTED);
sStateToEnvironment.put(VolumeInfo.STATE_MOUNTED_READ_ONLY, Environment.MEDIA_MOUNTED_READ_ONLY);
sStateToEnvironment.put(VolumeInfo.STATE_FORMATTING, Environment.MEDIA_UNMOUNTED);
sStateToEnvironment.put(VolumeInfo.STATE_EJECTING, Environment.MEDIA_EJECTING);
sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTABLE, Environment.MEDIA_UNMOUNTABLE);
sStateToEnvironment.put(VolumeInfo.STATE_REMOVED, Environment.MEDIA_REMOVED);
sStateToEnvironment.put(VolumeInfo.STATE_BAD_REMOVAL, Environment.MEDIA_BAD_REMOVAL);
sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTED, Intent.ACTION_MEDIA_UNMOUNTED);
sEnvironmentToBroadcast.put(Environment.MEDIA_CHECKING, Intent.ACTION_MEDIA_CHECKING);
sEnvironmentToBroadcast.put(Environment.MEDIA_MOUNTED, Intent.ACTION_MEDIA_MOUNTED);
sEnvironmentToBroadcast.put(Environment.MEDIA_MOUNTED_READ_ONLY, Intent.ACTION_MEDIA_MOUNTED);
sEnvironmentToBroadcast.put(Environment.MEDIA_EJECTING, Intent.ACTION_MEDIA_EJECT);
sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTABLE, Intent.ACTION_MEDIA_UNMOUNTABLE);
sEnvironmentToBroadcast.put(Environment.MEDIA_REMOVED, Intent.ACTION_MEDIA_REMOVED);
sEnvironmentToBroadcast.put(Environment.MEDIA_BAD_REMOVAL, Intent.ACTION_MEDIA_BAD_REMOVAL);
sStateToDescrip.put(VolumeInfo.STATE_UNMOUNTED, R.string.ext_media_status_unmounted);
sStateToDescrip.put(VolumeInfo.STATE_CHECKING, R.string.ext_media_status_checking);
sStateToDescrip.put(VolumeInfo.STATE_MOUNTED, R.string.ext_media_status_mounted);
sStateToDescrip.put(VolumeInfo.STATE_MOUNTED_READ_ONLY, R.string.ext_media_status_mounted_ro);
sStateToDescrip.put(VolumeInfo.STATE_FORMATTING, R.string.ext_media_status_formatting);
sStateToDescrip.put(VolumeInfo.STATE_EJECTING, R.string.ext_media_status_ejecting);
sStateToDescrip.put(VolumeInfo.STATE_UNMOUNTABLE, R.string.ext_media_status_unmountable);
sStateToDescrip.put(VolumeInfo.STATE_REMOVED, R.string.ext_media_status_removed);
sStateToDescrip.put(VolumeInfo.STATE_BAD_REMOVAL, R.string.ext_media_status_bad_removal);
}
然后通过 VolumeInfo.STATE_CHECKING 到:
base/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
private void onPublicVolumeStateChangedInternal(VolumeInfo vol) {
Log.d(TAG, "Notifying about public volume: " + vol.toString());
final Notification notif;
switch (vol.getState()) {
case VolumeInfo.STATE_UNMOUNTED:
notif = onVolumeUnmounted(vol);
break;
case VolumeInfo.STATE_CHECKING:
notif = onVolumeChecking(vol);
break;
case VolumeInfo.STATE_MOUNTED:
case VolumeInfo.STATE_MOUNTED_READ_ONLY:
notif = onVolumeMounted(vol);
break;
case VolumeInfo.STATE_FORMATTING:
notif = onVolumeFormatting(vol);
break;
case VolumeInfo.STATE_EJECTING:
notif = onVolumeEjecting(vol);
break;
case VolumeInfo.STATE_UNMOUNTABLE:
notif = onVolumeUnmountable(vol);
break;
case VolumeInfo.STATE_REMOVED:
notif = onVolumeRemoved(vol);
break;
case VolumeInfo.STATE_BAD_REMOVAL:
notif = onVolumeBadRemoval(vol);
break;
default:
notif = null;
break;
}
然后到同文件下的
private final StorageEventListener mListener = new StorageEventListener() {
@Override
public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
onVolumeStateChangedInternal(vol);
}
@Override
public void onVolumeRecordChanged(VolumeRecord rec) {
// Avoid kicking notifications when getting early metadata before
// mounted. If already mounted, we're being kicked because of a
// nickname or init'ed change.
final VolumeInfo vol = mStorageManager.findVolumeByUuid(rec.getFsUuid());
if (vol != null && vol.isMountedReadable()) {
onVolumeStateChangedInternal(vol);
}
}
@Override
public void onVolumeForgotten(String fsUuid) {
// Stop annoying the user
mNotificationManager.cancelAsUser(fsUuid, PRIVATE_ID, UserHandle.ALL);
}
@Override
public void onDiskScanned(DiskInfo disk, int volumeCount) {
onDiskScannedInternal(disk, volumeCount);
}
@Override
public void onDiskDestroyed(DiskInfo disk) {
onDiskDestroyedInternal(disk);
}
};
到 onVolumeStateChanged 这个方法的实现这里,然后到这个文件:
base/core/java/android/os/storage/StorageManager.java
@Override
public boolean handleMessage(Message msg) {
final SomeArgs args = (SomeArgs) msg.obj;
switch (msg.what) {
case MSG_STORAGE_STATE_CHANGED:
mCallback.onStorageStateChanged((String) args.arg1, (String) args.arg2,
(String) args.arg3);
args.recycle();
return true;
case MSG_VOLUME_STATE_CHANGED:
mCallback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
args.recycle();
return true;
case MSG_VOLUME_RECORD_CHANGED:
mCallback.onVolumeRecordChanged((VolumeRecord) args.arg1);
args.recycle();
return true;
case MSG_VOLUME_FORGOTTEN:
mCallback.onVolumeForgotten((String) args.arg1);
args.recycle();
return true;
case MSG_DISK_SCANNED:
mCallback.onDiskScanned((DiskInfo) args.arg1, args.argi2);
args.recycle();
return true;
case MSG_DISK_DESTROYED:
mCallback.onDiskDestroyed((DiskInfo) args.arg1);
args.recycle();
return true;
}
args.recycle();
return false;
}
进一步到同文件下的这里:
@Override
public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
final SomeArgs args = SomeArgs.obtain();
args.arg1 = vol;
args.argi2 = oldState;
args.argi3 = newState;
mHandler.obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget();
}
通过该类的继承继续往下找
private static class StorageEventListenerDelegate extends IMountServiceListener.Stub
依然是通过方法名 onVolumeStateChanged 搜索,到这里:
base/core/java/android/os/storage/IMountServiceListener.java
/**
* Detection state of USB Mass Storage has changed
*
* @param available true if a UMS host is connected.
*/
public void onUsbMassStorageConnectionChanged(boolean connected) throws RemoteException;
/**
* Storage state has changed.
*
* @param path The volume mount path.
* @param oldState The old state of the volume.
* @param newState The new state of the volume. Note: State is one of the
* values returned by Environment.getExternalStorageState()
*/
public void onStorageStateChanged(String path, String oldState, String newState)
throws RemoteException;
public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState)
throws RemoteException;
public void onVolumeRecordChanged(VolumeRecord rec) throws RemoteException;
public void onVolumeForgotten(String fsUuid) throws RemoteException;
public void onDiskScanned(DiskInfo disk, int volumeCount) throws RemoteException;
public void onDiskDestroyed(DiskInfo disk) throws RemoteException;
再进一步,还是通过刚才的方法名,找到这里:
base/services/core/java/com/android/server/MountService.java
@Override
public void handleMessage(Message msg) {
final SomeArgs args = (SomeArgs) msg.obj;
final int n = mCallbacks.beginBroadcast();
for (int i = 0; i < n; i++) {
final IMountServiceListener callback = mCallbacks.getBroadcastItem(i);
try {
invokeCallback(callback, msg.what, args);
} catch (RemoteException ignored) {
}
}
mCallbacks.finishBroadcast();
args.recycle();
}
private void invokeCallback(IMountServiceListener callback, int what, SomeArgs args)
throws RemoteException {
switch (what) {
case MSG_STORAGE_STATE_CHANGED: {
callback.onStorageStateChanged((String) args.arg1, (String) args.arg2,
(String) args.arg3);
break;
}
case MSG_VOLUME_STATE_CHANGED: {
callback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
break;
}
case MSG_VOLUME_RECORD_CHANGED: {
callback.onVolumeRecordChanged((VolumeRecord) args.arg1);
break;
}
case MSG_VOLUME_FORGOTTEN: {
callback.onVolumeForgotten((String) args.arg1);
break;
}
case MSG_DISK_SCANNED: {
callback.onDiskScanned((DiskInfo) args.arg1, args.argi2);
break;
}
case MSG_DISK_DESTROYED: {
callback.onDiskDestroyed((DiskInfo) args.arg1);
break;
}
}
}
通过这个关键字 MSG_VOLUME_STATE_CHANGED 定位到:
private void notifyVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
final SomeArgs args = SomeArgs.obtain();
args.arg1 = vol.clone();
args.argi2 = oldState;
args.argi3 = newState;
obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget();
}
进一步到这里:
private void onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState) {
// Remember that we saw this volume so we're ready to accept user
// metadata, or so we can annoy them when a private volume is ejected
if (vol.isMountedReadable() && !TextUtils.isEmpty(vol.fsUuid)) {
VolumeRecord rec = mRecords.get(vol.fsUuid);
if (rec == null) {
rec = new VolumeRecord(vol.type, vol.fsUuid);
rec.partGuid = vol.partGuid;
rec.createdMillis = System.currentTimeMillis();
if (vol.type == VolumeInfo.TYPE_PRIVATE) {
rec.nickname = vol.disk.getDescription();
}
mRecords.put(rec.fsUuid, rec);
writeSettingsLocked();
} else {
// Handle upgrade case where we didn't store partition GUID
if (TextUtils.isEmpty(rec.partGuid)) {
rec.partGuid = vol.partGuid;
writeSettingsLocked();
}
}
}
mCallbacks.notifyVolumeStateChanged(vol, oldState, newState);
// Do not broadcast before boot has completed to avoid launching the
// processes that receive the intent unnecessarily.
if (mBootCompleted && isBroadcastWorthy(vol)) {
final Intent intent = new Intent(VolumeInfo.ACTION_VOLUME_STATE_CHANGED);
intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.id);
intent.putExtra(VolumeInfo.EXTRA_VOLUME_STATE, newState);
intent.putExtra(VolumeRecord.EXTRA_FS_UUID, vol.fsUuid);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
mHandler.obtainMessage(H_INTERNAL_BROADCAST, intent).sendToTarget();
}
final String oldStateEnv = VolumeInfo.getEnvironmentForState(oldState);
final String newStateEnv = VolumeInfo.getEnvironmentForState(newState);
if (!Objects.equals(oldStateEnv, newStateEnv)) {
// Kick state changed event towards all started users. Any users
// started after this point will trigger additional
// user-specific broadcasts.
for (int userId : mSystemUnlockedUsers) {
if (vol.isVisibleForRead(userId)) {
final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, false);
mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
mCallbacks.notifyStorageStateChanged(userVol.getPath(), oldStateEnv,
newStateEnv);
}
}
}
if (vol.type == VolumeInfo.TYPE_PUBLIC && vol.state == VolumeInfo.STATE_EJECTING) {
// TODO: this should eventually be handled by new ObbVolume state changes
/*
* Some OBBs might have been unmounted when this volume was
* unmounted, so send a message to the handler to let it know to
* remove those from the list of mounted OBBS.
*/
mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
OBB_FLUSH_MOUNT_STATE, vol.path));
}
}
然后通过这个方法 onVolumeStateChangedLocked 找到这里:
case VoldResponseCode.VOLUME_STATE_CHANGED: {
if (cooked.length != 3) break;
final VolumeInfo vol = mVolumes.get(cooked[1]);
if (vol != null) {
final int oldState = vol.state;
final int newState = Integer.parseInt(cooked[2]);
vol.state = newState;
onVolumeStateChangedLocked(vol, oldState, newState);
}
break;
}
然后通过:
public static final int VOLUME_STATE_CHANGED = 651;
找到这个事件的发送端,这个时候通过类名 VoldResponseCode 很容易过度到 system/vold/这里
system/vold/ResponseCode.h
static const int VolumeStateChanged = 651;
然后找到这里:
system/vold/VolumeBase.cpp
void VolumeBase::setState(State state) {
mState = state;
notifyEvent(ResponseCode::VolumeStateChanged, StringPrintf("%d", mState));
}
然后到这里:
status_t VolumeBase::mount() {
if ((mState != State::kUnmounted) && (mState != State::kUnmountable)) {
LOG(WARNING) << getId() << " mount requires state unmounted or unmountable";
return -EBUSY;
}
setState(State::kChecking);
status_t res = doMount();
if (res == OK) {
setState(State::kMounted);
} else {
setState(State::kUnmountable);
}
return res;
}
通过doMount,结合硬盘是外挂设备,到这里:
system/vold/PublicVolume.h
class PublicVolume : public VolumeBase
找到
system/vold/PublicVolume.cpp
中,doMount的实现,根据硬盘格式ext4,找到实现,发现流程中会先check,然后mount,因此前面的提示,正在检查,是可以匹配实际动作的。
记录到此为止,中间跳跃部分可能存在错误之处,仅供参考。