[Android5.1]ActivityManagerService啓動過程分析

ActivityManagerService(簡稱AMS)是Android系統的關鍵服務之中的一個。它的主要做用例如如下:css

  • 管理系統中所有應用進程的整個生命週期
  • 管理應用進程中的Activity、Service、Broadcast和ContentProvider
  • 內存管理,低內存釋放等

AMS是一個服務端,定義了IBinder接口。其它的進程可以經過Binder機制與AMS進行通訊。java


AMS由system_server進程啓動的,並做爲一個獨立線程執行在system_server進程中。
如下就簡略分析一下AMS的啓動過程android

system_server啓動AMS

PATH:frameworks/base/services/java/com/android/server/SystemServer.java
啓動過程大體例如如下:
SystemServer.main()–>SystemServer.run()。數據庫

private void run() {
    ......
    startBootstrapServices();
    startOtherServices();
    ......
}

private void startBootstrapServices() {
    ......
    // 啓動AMS服務
    mActivityManagerService = mSystemServiceManager.startService(
            ActivityManagerService.Lifecycle.class).getService();

    // 初始化AMS的成員變量mSystemServiceManager 
    mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
    // 初始化AMS的成員變量mInstaller
    mActivityManagerService.setInstaller(installer);     
    ......
    // 主要是建立system_server相應的ProcessRecord,並初始化 
    mActivityManagerService.setSystemProcess();
    ......
}

private void startOtherServices() {
    ......
    // 主要是建立並註冊SettingsProvider
    mActivityManagerService.installSystemProviders();
    ......
    // 系統啓動前的準備工做,啓動SystemUI和Home界面等
    mActivityManagerService.systemReady(new Runnable() {...});
}

重點看一下mSystemServiceManager.startService():markdown

public SystemService startService(String className) {
    final Class<SystemService> serviceClass;
    try {
        serviceClass = (Class<SystemService>)Class.forName(className);
    } catch (ClassNotFoundException ex) {
        ......
    }
    return startService(serviceClass);
}

public <T extends SystemService> T startService(Class<T> serviceClass) {
    final String name = serviceClass.getName();
    Slog.i(TAG, "Starting " + name);

    // Create the service.
    if (!SystemService.class.isAssignableFrom(serviceClass)) {
        throw new RuntimeException("Failed to create " + name
                + ": service must extend " + SystemService.class.getName());
    }
    final T service;
    try {
        Constructor<T> constructor = serviceClass.getConstructor(Context.class);
        //建立ActivityManagerService.Lifecycle對象
        service = constructor.newInstance(mContext);
    } catch (InstantiationException ex) {
        ......
    }

    // 加入到成員變量mServices中
    mServices.add(service);

    // Start it.
    try {
        // 執行ActivityManagerService.Lifecycle.onStart()
        service.onStart();
    } catch (RuntimeException ex) {
        throw new RuntimeException("Failed to start service " + name
                + ": onStart threw an exception", ex);
    }
    return service;
}

ActivityManagerService.Lifecycle

PATH:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.javaapp

public static final class Lifecycle extends SystemService {
    private final ActivityManagerService mService;

    public Lifecycle(Context context) {
        super(context);
        // 建立ActivityManagerServiceEx對象
        mService = new ActivityManagerServiceEx(context);
    }

    @Override
    public void onStart() {
        mService.start(); //執行ActivityManagerServiceEx.start()
    }

    public ActivityManagerService getService() {
        return mService;
    }
}

當中,ActivityManagerServiceEx是ActivityManagerService的子類,定義例如如下:ide

public final class ActivityManagerServiceEx extends ActivityManagerService {
    ......
    public ActivityManagerServiceEx(Context systemContext) {
       super(systemContext);
       mIsInHome = true;
    }
    ......
}

這樣,在new ActivityManagerServiceEx()時候。會調用到其父類ActivityManagerService的構造函數。函數

ActivityManagerService構造函數

PATH:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.javaoop

佈局

public ActivityManagerService(Context systemContext) { mContext = systemContext; mFactoryTest = FactoryTest.getMode(); mSystemThread = ActivityThread.currentActivityThread(); Slog.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass()); //建立名稱爲「ActivityManager」的消息循環線程。 mHandlerThread = new ServiceThread(TAG, android.os.Process.THREAD_PRIORITY_FOREGROUND, false /*allowIo*/); mHandlerThread.start(); //將該線程綁定到MainHandler,由MainHandler完畢消息的處理 mHandler = new MainHandler(mHandlerThread.getLooper()); //建立前臺廣播接收器,執行超時爲10s mFgBroadcastQueue = new BroadcastQueue(this, mHandler, "foreground", BROADCAST_FG_TIMEOUT, false); //建立後臺廣播接收器。執行超時爲60s mBgBroadcastQueue = new BroadcastQueue(this, mHandler, "background", BROADCAST_BG_TIMEOUT, true); mBroadcastQueues[0] = mFgBroadcastQueue; mBroadcastQueues[1] = mBgBroadcastQueue; mServices = new ActiveServicesEx(this); mProviderMap = new ProviderMap(this); //新建/data/system文件夾 File dataDir = Environment.getDataDirectory(); File systemDir = new File(dataDir, "system"); systemDir.mkdirs(); //建立BatteryStatsService服務 mBatteryStatsService = new BatteryStatsService(systemDir, mHandler); mBatteryStatsService.getActiveStatistics().readLocked(); mBatteryStatsService.getActiveStatistics().writeAsyncLocked(); mOnBattery = DEBUG_POWER ?

true : mBatteryStatsService.getActiveStatistics().getIsOnBattery(); mBatteryStatsService.getActiveStatistics().setCallback(this); //建立ProcessStatsService服務 mProcessStats = new ProcessStatsService(this, new File(systemDir, "procstats")); //建立AppOpsService服務 mAppOpsService = new AppOpsService(new File(systemDir, "appops.xml"), mHandler); //建立AtomicFile文件 mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml")); // User 0 is the first and only user that runs at boot. mStartedUsers.put(0, new UserStartedState(new UserHandle(0), true)); mUserLru.add(Integer.valueOf(0)); updateStartedUserArrayLocked(); GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", ConfigurationInfo.GL_ES_VERSION_UNDEFINED); mTrackingAssociations = "1".equals(SystemProperties.get("debug.track-associations")); mConfiguration.setToDefaults(); mConfiguration.locale = Locale.getDefault(); mConfigurationSeq = mConfiguration.seq = 1; mProcessCpuTracker.init(); mCompatModePackages = new CompatModePackages(this, systemDir, mHandler); mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler); mStackSupervisor = new ActivityStackSupervisor(this); mTaskPersister = new TaskPersister(systemDir, mStackSupervisor); mProcessCpuThread = new Thread("CpuTracker") { @Override public void run() { while (true) { try { try { synchronized(this) { final long now = SystemClock.uptimeMillis(); long nextCpuDelay = (mLastCpuTime.get()+MONITOR_CPU_MAX_TIME)-now; long nextWriteDelay = (mLastWriteTime+BATTERY_STATS_TIME)-now; //Slog.i(TAG, "Cpu delay=" + nextCpuDelay // + ", write delay=" + nextWriteDelay); if (nextWriteDelay < nextCpuDelay) { nextCpuDelay = nextWriteDelay; } if (nextCpuDelay > 0) { mProcessCpuMutexFree.set(true); this.wait(nextCpuDelay); } } } catch (InterruptedException e) { } updateCpuStatsNow(); } catch (Exception e) { Slog.e(TAG, "Unexpected exception collecting process stats", e); } } } }; Watchdog.getInstance().addMonitor(this); Watchdog.getInstance().addThread(mHandler); }

首先,建立了一個名爲「ActivityManager」的消息循環線程,不斷地接收其它進程發給AMS的消息;並把該消息循環線程與MainHandler綁定,這樣,由MainHandler完畢消息的詳細處理。


而後,建立了一些服務,並在/data/system文件夾下建立該服務需要的文件或文件夾,詳細例如如下:

服務 服務說明 文件 文件說明
BatteryStatsService 電池狀態管理 /data/system/batterystats.bin 記錄包含電壓在內的各類電池信息
ProcessStatsService 進程狀態管理 /data/system/procstats 記錄各個進程的狀態信息
AppOpsService 應用操做權限管理 /data/system/appops.xml 存儲各個app的權限設置和操做信息

另外,還建立了一個AtomicFile類型的文件mGrantFile ,文件路徑爲/data/system/urigrants.xml。
AtomicFile文件是經過建立一個備份文件來執行原子性操做的幫助類,保證文件讀寫的完整。
這樣。system_server就完畢AMS的構造和啓動。
回到system_server中,咱們看到又調用了AMS的setSystemProcess方法。如下就分析一下該方法。

ActivityManagerService.setSystemProcess

public void setSystemProcess() {
    try {
        ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
        ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
        ServiceManager.addService("meminfo", new MemBinder(this));
        ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
        ServiceManager.addService("dbinfo", new DbBinder(this));
        if (MONITOR_CPU_USAGE) {
            ServiceManager.addService("cpuinfo", new CpuBinder(this));
        }
        ServiceManager.addService("permission", new PermissionController(this));

        ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
                "android", STOCK_PM_FLAGS);
        mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());

        synchronized (this) {
            ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0);
            app.persistent = true;
            app.pid = MY_PID;
            app.maxAdj = ProcessList.SYSTEM_ADJ;
            app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
            mProcessNames.put(app.processName, app.uid, app);
            synchronized (mPidsSelfLocked) {
                mPidsSelfLocked.put(app.pid, app);
            }
            updateLruProcessLocked(app, false, null);
            updateOomAdjLocked();
        }
    } catch (PackageManager.NameNotFoundException e) {
        throw new RuntimeException(
                "Unable to find android system package", e);
    }
}

首先,向SystemServiceManager中加入了若干個服務:

服務 服務說明
activity AMS服務自己
procstats 進程狀態管理
meminfo 獲取內存信息
gfxinfo 監控分析GPU profiling信息
dbinfo 數據庫相關服務
cpuinfo 獲取cpu相關信息
permission 權限控制相關服務

而後。調用PMS的getApplicationInfo接口,獲取名爲」android」的應用程序信息。包名爲」android」的apk即/system/framework/framework-res.apk。裏面保存着系統GUI美化的相關文件,包含圖標,彈出對話框的樣式,動做特效,界面佈局等。調用installSystemApplicationInfo載入framework-res.apk文件。
接着,調用newProcessRecordLocked新建一個ProcessRecord 對象app。ProcessRecord用來描寫敘述一個進程的所有信息,包含該進程的所有activity和service等。在這裏就是system_server(AMS就是在system_server進程中執行的)。

建立後,對app的一些成員變量進行初始化。包含設置爲常駐內存執行;設置system_server的pid等。
最後。調用mProcessNames.put()將建立的ProcessRecord 對象app加入到ProcessMap< ProcessRecord >類型的成員變量mProcessNames中。

這裏。app.processName=「system」。

這樣,AMS就獲得了system_server的ProcessRecord,之後AMS也可以管理system_server了。


繼續回到system_server中。咱們看到又調用了AMS的installSystemProviders方法,如下就分析一下該方法。

ActivityManagerService.installSystemProviders

public final void installSystemProviders() {
    List<ProviderInfo> providers;
    synchronized (this) {
        ProcessRecord app = mProcessNames.get("system", Process.SYSTEM_UID);
        providers = generateApplicationProvidersLocked(app);
        if (providers != null) {
            for (int i=providers.size()-1; i>=0; i--) {
                ProviderInfo pi = (ProviderInfo)providers.get(i);
                if ((pi.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) {
                    Slog.w(TAG, "Not installing system proc provider " + pi.name
                            + ": not system .apk");
                    providers.remove(i);
                }
            }
        }
    }
    if (providers != null) {
        mSystemThread.installSystemProviders(providers);
    }

    mCoreSettingsObserver = new CoreSettingsObserver(this);

    //mUsageStatsService.monitorPackages();
}

首先,取出在setSystemProcess()中put到mProcessNames中的ProcessRecord對象app,即system_server的進程信息。


而後,調用generateApplicationProvidersLocked:

private final List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) {
    List<ProviderInfo> providers = null;
    try {
        providers = AppGlobals.getPackageManager().
            queryContentProviders(app.processName, app.uid,
                    STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS);
    } catch (RemoteException ex) {
    }
    if (DEBUG_MU)
        Slog.v(TAG_MU, "generateApplicationProvidersLocked, app.info.uid = " + app.uid);
    int userId = app.userId;
    if (providers != null) {
        int N = providers.size();
        app.pubProviders.ensureCapacity(N + app.pubProviders.size());
        for (int i=0; i<N; i++) {
            ProviderInfo cpi =
                (ProviderInfo)providers.get(i);
            boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
                    cpi.name, cpi.flags);
            if (singleton && UserHandle.getUserId(app.uid) != 0) {
                providers.remove(i);
                N--;
                i--;
                continue;
            }

            ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
            ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId);
            if (cpr == null) {
                cpr = new ContentProviderRecord(this, cpi, app.info, comp, singleton);
                mProviderMap.putProviderByClass(comp, cpr);
            }
            if (DEBUG_MU)
                Slog.v(TAG_MU, "generateApplicationProvidersLocked, cpi.uid = " + cpr.uid);
            app.pubProviders.put(cpi.name, cpr);
            if (!cpi.multiprocess || !"android".equals(cpi.packageName)) {
                app.addPackage(cpi.applicationInfo.packageName, cpi.applicationInfo.versionCode,
                        mProcessStats);
            }
            ensurePackageDexOpt(cpi.applicationInfo.packageName);
        }
    }
    return providers;
}

首先,調用PMS的queryContentProviders查找processName=「system」和uid=SYSTEM_UID的provider。即SettingsProvider。並獲取其ProviderInfo 。
而後新建一個ContentProviderRecord(描寫敘述一個ContentProvider,這裏爲SettingsProvider),並加入到AMS的成員變量mProviderMap和ProcessRecord對象的pubProviders中。


最後將SettingsProvider所在的package加入到ProcessRecord對象的pkglist中。

繼續回到installSystemProviders()。調用installSystemProviders,先建立後註冊SettingsProvider。這樣,其它進程就可以調用SettingsProvider。查詢或改動一些系統設置了。

在installSystemProviders()的最後,註冊一個ContentObserver來監聽SettingsProvider中的狀態變化。


繼續回到system_server中。咱們看到又調用了AMS的systemReady方法。如下就分析一下該方法。

ActivityManagerService.systemReady

該函數主要完畢瞭如下幾件事:

  • 在當前執行的進程中,查找在systemReady以前不一樣意啓動的進程。而後調用removeProcessLocked()終止該進程並釋放資源。推斷條件例如如下:
boolean isAllowedWhileBooting(ApplicationInfo ai) {
    return (ai.flags&ApplicationInfo.FLAG_PERSISTENT) != 0;
}
  • 載入若干配置信息和資源信息,代碼例如如下:
retrieveSettings();
loadResourcesOnSystemReady();

private void retrieveSettings() {
    final ContentResolver resolver = mContext.getContentResolver();
    String debugApp = Settings.Global.getString(
        resolver, Settings.Global.DEBUG_APP);
    boolean waitForDebugger = Settings.Global.getInt(
        resolver, Settings.Global.WAIT_FOR_DEBUGGER, 0) != 0;
    boolean alwaysFinishActivities = Settings.Global.getInt(
        resolver, Settings.Global.ALWAYS_FINISH_ACTIVITIES, 0) != 0;
    boolean forceRtl = Settings.Global.getInt(
            resolver, Settings.Global.DEVELOPMENT_FORCE_RTL, 0) != 0;
    ......
}

private void loadResourcesOnSystemReady() {
    final Resources res = mContext.getResources();
    mHasRecents = res.getBoolean(com.android.internal.R.bool.config_hasRecents);
    mThumbnailWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
    mThumbnailHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
}
  • 調用回調函數goingCallback.run()。主要完畢兩件事:執行其它系統服務的systemready()或systemRunning();啓動SystemUI。


    goingCallback.run()的實現在systemserver.java中:

mActivityManagerService.systemReady(new Runnable() {
    @Override
    public void run() {
        Slog.i(TAG, "Making services ready");
        mSystemServiceManager.startBootPhase(
                SystemService.PHASE_ACTIVITY_MANAGER_READY);

        try {
            mActivityManagerService.startObservingNativeCrashes();
        } catch (Throwable e) {
            reportWtf("observing native crashes", e);
        }

        Slog.i(TAG, "WebViewFactory preparation");
        WebViewFactory.prepareWebViewInSystemServer();

        try {
            startSystemUi(context); //啓動SystemUI
        } catch (Throwable e) {
            reportWtf("starting System UI", e);
        }
        try {
            if (mountServiceF != null) mountServiceF.systemReady();
        } catch (Throwable e) {
            reportWtf("making Mount Service ready", e);
        }
        try {
            if (networkScoreF != null) networkScoreF.systemReady();
        } catch (Throwable e) {
            reportWtf("making Network Score Service ready", e);
        }
        try {
            if (networkManagementF != null) networkManagementF.systemReady();
        } catch (Throwable e) {
            reportWtf("making Network Managment Service ready", e);
        }
        try {
            if (networkStatsF != null) networkStatsF.systemReady();
        } catch (Throwable e) {
            reportWtf("making Network Stats Service ready", e);
        }
        try {
            if (networkPolicyF != null) networkPolicyF.systemReady();
        } catch (Throwable e) {
            reportWtf("making Network Policy Service ready", e);
        }
        try {
            if (connectivityF != null) connectivityF.systemReady();
        } catch (Throwable e) {
            reportWtf("making Connectivity Service ready", e);
        }
        try {
            if (audioServiceF != null) audioServiceF.systemReady();
        } catch (Throwable e) {
            reportWtf("Notifying AudioService running", e);
        }
        Watchdog.getInstance().start();

        // It is now okay to let the various system services start their
        // third party code...
        mSystemServiceManager.startBootPhase(
                SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);

        try {
            if (wallpaperF != null) wallpaperF.systemRunning();
        } catch (Throwable e) {
            reportWtf("Notifying WallpaperService running", e);
        }
        try {
            if (immF != null) immF.systemRunning(statusBarF);
        } catch (Throwable e) {
            reportWtf("Notifying InputMethodService running", e);
        }
        try {
            if (locationF != null) locationF.systemRunning();
        } catch (Throwable e) {
            reportWtf("Notifying Location Service running", e);
        }
        try {
            if (countryDetectorF != null) countryDetectorF.systemRunning();
        } catch (Throwable e) {
            reportWtf("Notifying CountryDetectorService running", e);
        }
        try {
            if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemRunning();
        } catch (Throwable e) {
            reportWtf("Notifying NetworkTimeService running", e);
        }
        try {
            if (commonTimeMgmtServiceF != null) {
                commonTimeMgmtServiceF.systemRunning();
            }
        } catch (Throwable e) {
            reportWtf("Notifying CommonTimeManagementService running", e);
        }
        try {
            if (textServiceManagerServiceF != null)
                textServiceManagerServiceF.systemRunning();
        } catch (Throwable e) {
            reportWtf("Notifying TextServicesManagerService running", e);
        }
        try {
            if (atlasF != null) atlasF.systemRunning();
        } catch (Throwable e) {
            reportWtf("Notifying AssetAtlasService running", e);
        }
        try {
            // TODO(BT) Pass parameter to input manager
            if (inputManagerF != null) inputManagerF.systemRunning();
        } catch (Throwable e) {
            reportWtf("Notifying InputManagerService running", e);
        }
        try {
            if (telephonyRegistryF != null) telephonyRegistryF.systemRunning();
        } catch (Throwable e) {
            reportWtf("Notifying TelephonyRegistry running", e);
        }

        try {
            if (mediaRouterF != null) mediaRouterF.systemRunning();
        } catch (Throwable e) {
            reportWtf("Notifying MediaRouterService running", e);
        }

        if (SystemProperties.get("persist.support.securetest").equals("1")) {
            if (securityF != null) {
                try {
                    securityF.systemReady(context);
                } catch (Throwable e) {
                    reportWtf("Security Service ready", e);
                }
            }
        }
    }
});
  • 調用addAppLocked啓動那些聲明爲「FLAG_SYSTEM|FLAG_PERSISTENT」的應用程序:
final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated,
            String abiOverride) {
    ProcessRecord app;
    if (!isolated) {
        app = getProcessRecordLocked(info.processName, info.uid, true);
    } else {
        ......
    }
    ......
    // This package really, really can not be stopped.
    try {
        AppGlobals.getPackageManager().setPackageStoppedState(
                info.packageName, false, UserHandle.getUserId(app.uid));
    } catch (RemoteException e) {
    } catch (IllegalArgumentException e) {
        Slog.w(TAG, "Failed trying to unstop package "
                + info.packageName + ": " + e);
    }
    //推斷條件
    if ((info.flags&(ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT))
            == (ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT)) {
        app.persistent = true;
        app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
    }
    //假設該app的IApplicationThread等於null,並且沒有在ArrayList<ProcessRecord>類型
    //的成員變量mPersistentStartingProcesses中,
    //啓動該app,並把它加入到mPersistentStartingProcesses中。
    if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
        mPersistentStartingProcesses.add(app);
        startProcessLocked(app, "added application", app.processName, abiOverride,
                null /* entryPoint */, null /* entryPointArgs */);
    }

    return app;
}
  • 廣播兩個消息:ACTION_USER_STARTED和ACTION_USER_STARTING。標識用戶已經started
Intent intent = new Intent(Intent.ACTION_USER_STARTED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
        | Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_USER_HANDLE, mCurrentUserId);
broadcastIntentLocked(null, null, intent,
        null, null, 0, null, null, null, AppOpsManager.OP_NONE,
        false, false, MY_PID, Process.SYSTEM_UID, mCurrentUserId);
intent = new Intent(Intent.ACTION_USER_STARTING);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
intent.putExtra(Intent.EXTRA_USER_HANDLE, mCurrentUserId);
broadcastIntentLocked(null, null, intent,
    null, new IIntentReceiver.Stub() {
       @Override
        public void performReceive(Intent intent, int resultCode, String data,
                Bundle extras, boolean ordered, boolean sticky, int sendingUser)
                throws RemoteException {
        }
        }, 0, null, null,
    INTERACT_ACROSS_USERS, AppOpsManager.OP_NONE,
    true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
} catch (Throwable t) {
......
  • 調用startHomeActivityLocked()啓動HOME界面。

這樣,ActivityManagerService的啓動和初始化就完畢了。

相關文章
相關標籤/搜索