MountService实现

相关文件

frameworks/base/services/java/com/android/server/NativeDaemonConnector.java
frameworks/base/services/java/com/android/server/MountService.java
frameworks/base/services/java/com/android/server/PackageManagerService.java

frameworks/base/core/java/android/os/storage/StorageManager.java
frameworks/base/core/java/android/os/storage/MountServiceListener.java

MountService的功能

MountService的初始化

public MountService(Context context) {

    // XXX: This will go away soon in favor of IMountServiceObserver
    mPms = (PackageManagerService) ServiceManager.getService("package");

    // 注册package的broadcastreceiver
    mIntentFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
    mIntentFilter.addAction(Intent.ACTION_PACKAGE_INSTALL);
    mIntentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);*/
    mIntentFilter.addDataScheme("package");
    mIntentFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);
    mIntentFilter.addDataScheme("file");
    mContext.registerReceiver(mPackageBroadcastReceiver, mIntentFilter, null, null);
    
    // 注册启动的BroadcastReceiver
    mContext.registerReceiver(mBroadcastReceiver,
         new IntentFilter(Intent.ACTION_BOOT_COMPLETED) , null, null);

    // 注册内部处理的handler
    mHandlerThread = new HandlerThread("MountService");
    mHandlerThread.start();
    mHandler = new MountServiceHandler(mHandlerThread.getLooper());

    // Add OBB Action Handler to MountService thread.
    mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper());

    // 对于模拟器做特别处理
    if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
        mReady = true;
        mUmsEnabling = true;
        return;
    }

    // 开启与vold通讯的工作线程 
    mConnector = new NativeDaemonConnector(this, "vold",
            PackageManagerService.MAX_CONTAINERS * 2, VOLD_TAG);
    mReady = false;
    Thread thread = new Thread(mConnector, VOLD_TAG);
    thread.start();
}

NativeDaemonConnector工作线程

向下注册的回调

INativeDaemonConnectorCallbacks

interface INativeDaemonConnectorCallbacks {
    void onDaemonConnected();
    boolean onEvent(int code, String raw, String[] cooked);
}

onDaemonConnected

public void onDaemonConnected() {
    // 因为MountService会回调NativeDaemonConnector的方法,因此工作在线程模式 
    new Thread() {
        public void run() {
            try {
                // 使用"volume list"指令查询设备状态
                String[] vols = mConnector.doListCommand(
                    "volume list", VoldResponseCode.VolumeListResult);

                for (String volstr : vols) {
                    String[] tok = volstr.split(" ");
                    ......
                   if( tok[1].equals(removableSDPath) ) {
                        int st = Integer.parseInt(tok[2]);
                        if (st == VolumeState.NoMedia) {
                           removableSDState = Environment.MEDIA_REMOVED;
                        } else if (st == VolumeState.Idle) {
                           removableSDState = Environment.MEDIA_UNMOUNTED;
                        } else if (st == VolumeState.Mounted) {
                           removableSDState = Environment.MEDIA_MOUNTED;
                           Slog.i(TAG, "Media already mounted on daemon connection");
                        } else if (st == VolumeState.Shared) {
                           removableSDState = Environment.MEDIA_SHARED;
                           Slog.i(TAG, "Media shared on daemon connection");
                        } else {
                            throw new Exception(String.format("Unexpected state %d", st));
                        }
                        
                        if (removableSDState != null) {
                            ......
                            updatePublicVolumeState(removableSDPath, removableSDState);
                        }
                    } else {
                        continue;
                    }
                }
            } catch (Exception e) {
                updatePublicVolumeState(removableSDPath, Environment.MEDIA_REMOVED);
            }

            try {
                boolean avail = doGetShareMethodAvailable("ums");
                notifyShareAvailabilityChange("ums", avail);
            } catch (Exception ex) {
              }
            mReady = true;
       }
    }.start();
}

onEvent

public boolean onEvent(int code, String raw, String[] cooked) {
    ......
    if (code == VoldResponseCode.VolumeStateChange) {
        /*
         * One of the volumes we're managing has changed state.
         * Format: "NNN Volume <label> <path> state changed
         * from <old_#> (<old_str>) to <new_#> (<new_str>)"
         */
        notifyVolumeStateChange( cooked[2], cooked[3], Integer.parseInt(cooked[7]),
                                Integer.parseInt(cooked[10]));
    } else if (code == VoldResponseCode.ShareAvailabilityChange) {
        // FMT: NNN Share method <method> now <available|unavailable>
        boolean avail = false;
        if (cooked[5].equals("available")) {
            avail = true;
        }
        notifyShareAvailabilityChange(cooked[3], avail);
    } else if ((code == VoldResponseCode.VolumeDiskInserted) ||
               (code == VoldResponseCode.VolumeOKtoMount) ||
               (code == VoldResponseCode.VolumeDiskRemoved) ||
               (code == VoldResponseCode.VolumeBadRemoval)) {
        // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
        // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
        // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
        if (code == VoldResponseCode.VolumeOKtoMount) {
            ...... 
        } else if (code == VoldResponseCode.VolumeDiskRemoved) {
            /*
             * This event gets trumped if we're already in BAD_REMOVAL state
             */
            ...... 
            updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
            in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
            mContext.sendBroadcast(in);
            ......
            updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
            in = new Intent(Intent.ACTION_MEDIA_REMOVED, Uri.parse("file://" + path));
        } else if (code == VoldResponseCode.VolumeBadRemoval) {
            ...... 
            /* Send the media unmounted event first */
            updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
            in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
            mContext.sendBroadcast(in);
            ......
            updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL);
            in = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL, Uri.parse("file://" + path));
        } else {
        }
    } else {
        return false;
    }

    if (in != null) {
        mContext.sendBroadcast(in);
    }
    return true;
}

updatePublicVolumeState

private void updatePublicVolumeState(String path, String state) {
    ......
    if ( path.equals(Environment.getRemovableStorageDirectory().getPath()) ) {
        // Update state on PackageManager
        if (Environment.MEDIA_UNMOUNTED.equals(state)) {
            mPms.updateExternalMediaStatus(false, false);
         
            /*
             * 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,
                    path));
        } else if (Environment.MEDIA_MOUNTED.equals(state)) {
            mPms.updateExternalMediaStatus(true, false);
        }
        ...... 
        synchronized (mListeners) {
            for (int i = mListeners.size() -1; i >= 0; i--) {
                MountServiceBinderListener bl = mListeners.get(i);
                try {
                    bl.mListener.onStorageStateChanged(path, oldState, state);
                } catch (RemoteException rex) {
                    mListeners.remove(i);
                } catch (Exception ex) {
                }
            }
        }
    } else {
        return;
    }
    return;
}

注册上层的回调

registerListener

public void registerListener(IMountServiceListener listener) {
    synchronized (mListeners) {
        MountServiceBinderListener bl = new MountServiceBinderListener(listener);
        try {
            listener.asBinder().linkToDeath(bl, 0);
            mListeners.add(bl);
        } catch (RemoteException rex) {
        }
    }
}

MountService.MountServiceBinderListener

private final class MountServiceBinderListener implements IBinder.DeathRecipient {
    final IMountServiceListener mListener;

    MountServiceBinderListener(IMountServiceListener listener) {
        mListener = listener;

    }

    public void binderDied() {
        synchronized (mListeners) {
            mListeners.remove(this);
            mListener.asBinder().unlinkToDeath(this, 0);
        }
    }
}

Media管理

updateExternalMediaStatus

// PackageManagerService.java
public void updateExternalMediaStatus(final boolean mediaStatus, final boolean reportStatus) {
    if (Binder.getCallingUid() != Process.SYSTEM_UID) {
       throw new SecurityException("Media status can only be updated by the system");
    }
    synchronized (mPackages) {
       if (mediaStatus == mMediaMounted) {
           Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS,
                   reportStatus ? 1 : 0, -1);
           mHandler.sendMessage(msg);
           return;
       }
       mMediaMounted = mediaStatus;
    }
    // Queue up an async operation since the package installation may take a little while.
    mHandler.post(new Runnable() {
       public void run() {
           mHandler.removeCallbacks(this);
           updateExternalMediaStatusInner(mediaStatus, reportStatus);
       }
    });
}

updateExternalMediaStatusInner

// PackageManagerService.java
private void updateExternalMediaStatusInner(boolean mediaStatus,
       boolean reportStatus) {
    // Collection of uids
    int uidArr[] = null;
    // Collection of stale containers
    HashSet<String> removeCids = new HashSet<String>();
    // Collection of packages on external media with valid containers.
    HashMap<SdInstallArgs, String> processCids = new HashMap<SdInstallArgs, String>();
    // Get list of secure containers.
    final String list[] = PackageHelper.getSecureContainerList();
    if (list == null || list.length == 0) {
    } else {
        // Process list of secure containers and categorize them
        // as active or stale based on their package internal state.
        int uidList[] = new int[list.length];
        int num = 0;
        synchronized (mPackages) {
            for (String cid : list) {
                SdInstallArgs args = new SdInstallArgs(cid);
                String pkgName = args.getPackageName();
                if (pkgName == null) {
                    removeCids.add(cid);
                    continue;
                }
               
                PackageSetting ps = mSettings.mPackages.get(pkgName);
                if (ps != null && ps.codePathString != null &&
                        ps.codePathString.equals(args.getCodePath())) {
                    // We do have a valid package installed on sdcard
                    processCids.put(args, ps.codePathString);
                    int uid = ps.userId;
                    if (uid != -1) {
                        uidList[num++] = uid;
                    }
                } else {
                   removeCids.add(cid);
                }
            }
        }
        ...... 
    }
    // Process packages with valid entries.
    if (mediaStatus) {
       loadMediaPackages(processCids, uidArr, removeCids);
       startCleaningPackages();
    } else {
       unloadMediaPackages(processCids, uidArr, reportStatus);
    }
}

PackageHelper工具

// PackageHelper.java

/**
 * Constants used internally between the PackageManager
 * and media container service transports.
 * Some utility methods to invoke MountService api.
 */
public class PackageHelper {
    ......
    public static IMountService getMountService() {
        ......
    }

    public static String createSdDir(long sizeBytes, String cid,
        ......
    }

    public static String mountSdDir(String cid, String key, int ownerUid) {
        ......
    }

    public static boolean unMountSdDir(String cid) {
        ......
    }

    public static boolean renameSdDir(String oldId, String newId) {
        ......
    }

    public static String getSdDir(String cid) {
        ...... 
    }

    public static boolean finalizeSdDir(String cid) {
        ......
    }

    public static boolean destroySdDir(String cid) {
        ......
    }

    public static String[] getSecureContainerList() {
        ...... 
    }

    public static boolean isContainerMounted(String cid) {
        ......
    }
}