Android L Phone 启动流程

1、在ActivityManagerService 的SystemReady中启动所有persist属性的应用 ,其中就包含TeleService,且在Launcher前启动!


        synchronized (this) {
            if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
                try {
                    List apps = AppGlobals.getPackageManager().
                        getPersistentApplications(STOCK_PM_FLAGS);
                    if (apps != null) {
                        int N = apps.size();
                        int i;
                        for (i=0; i<N; i++) {
                            ApplicationInfo info
                                = (ApplicationInfo)apps.get(i);
                            if (info != null &&
                                    !info.packageName.equals("android")) {
                                addAppLocked(info, false, null /* ABI override */);
                            }
                        }
                    }
                } catch (RemoteException ex) {
                    // pm is in same process, this will never happen.
                }
            }

            // Start up initial activity.
            mBooting = true;
            startHomeActivityLocked(mCurrentUserId);

            try {
                if (AppGlobals.getPackageManager().hasSystemUidErrors()) {
                    Message msg = Message.obtain();
                    msg.what = SHOW_UID_ERROR_MSG;
                    mHandler.sendMessage(msg);
                }
            } catch (RemoteException e) {
            }


2 、于是会启动Telephony中的PhoneApp


    @Override
    public void onCreate() {
        if (UserHandle.myUserId() == 0) {
            // We are running as the primary user, so should bring up the
            // global phone state.
            mPhoneGlobals = new PhoneGlobals(this);
            mPhoneGlobals.onCreate();

            mTelephonyGlobals = new TelephonyGlobals(this);
            mTelephonyGlobals.onCreate();
        }
    }

它的主要工作便是启动PhoneGlobals,这是一个非常重要的类:

在这里初始化了Phone的framework层的东西,是的,就是这样上上下下来回调,起到承上启下的作用。

而且PhoneGlobals还采用了单例模式!来看重要的onCreate方法:

  public void onCreate() {
        if (VDBG) Log.v(LOG_TAG, "onCreate()...");

        ContentResolver resolver = getContentResolver();

        // Cache the "voice capable" flag.
        // This flag currently comes from a resource (which is
        // overrideable on a per-product basis):
        sVoiceCapable =
                getResources().getBoolean(com.android.internal.R.bool.config_voice_capable);
        // ...but this might eventually become a PackageManager "system
        // feature" instead, in which case we'd do something like:
        // sVoiceCapable =
        //   getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_VOICE_CALLS);

        if (phone == null) {
            // Initialize the telephony framework
            <span style="background-color: rgb(255, 255, 102);">PhoneFactory.makeDefaultPhones(this);</span>

            int numPhones = TelephonyManager.getDefault().getPhoneCount();
            if(numPhones > 1) PrimarySubSelectionController.init(this);

            // Get the default phone
           <span style="background-color: rgb(255, 255, 51);"> phone = PhoneFactory.getDefaultPhone();</span>

            // Start TelephonyDebugService After the default phone is created.
            Intent intent = new Intent(this, TelephonyDebugService.class);
            startService(intent);

            mPhones = new PhoneProxy[numPhones];
            mPhones = PhoneFactory.getPhones();

            <span style="background-color: rgb(255, 255, 102);">mCM = CallManager.getInstance();</span>
            for (Phone ph : mPhones) {
                <span style="background-color: rgb(255, 255, 51);">mCM.registerPhone(ph);</span>
            }

            // Create the NotificationMgr singleton, which is used to display
            // status bar icons and control other status bar behavior.
            notificationMgr = NotificationMgr.init(this);

            mHandler.sendEmptyMessage(EVENT_START_SIP_SERVICE);

            int phoneType = phone.getPhoneType();

            if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
                // Create an instance of CdmaPhoneCallState and initialize it to IDLE
                cdmaPhoneCallState = new CdmaPhoneCallState();
                cdmaPhoneCallState.CdmaPhoneCallStateInit();
            }

            // before registering for phone state changes
            mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
            mWakeLock = mPowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, LOG_TAG);
            // lock used to keep the processor awake, when we don't care for the display.
            mPartialWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK
                    | PowerManager.ON_AFTER_RELEASE, LOG_TAG);

            mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);

            // get a handle to the service so that we can use it later when we
            // want to set the poke lock.
            mPowerManagerService = IPowerManager.Stub.asInterface(
                    ServiceManager.getService("power"));

            // Get UpdateLock to suppress system-update related events (e.g. dialog show-up)
            // during phone calls.
            mUpdateLock = new UpdateLock("phone");

            if (DBG) Log.d(LOG_TAG, "onCreate: mUpdateLock: " + mUpdateLock);

            CallLogger callLogger = new CallLogger(this, new CallLogAsync());

            callGatewayManager = CallGatewayManager.getInstance();

            // Create the CallController singleton, which is the interface
            // to the telephony layer for user-initiated telephony functionality
            // (like making outgoing calls.)
            <span style="background-color: rgb(255, 255, 102);">callController = CallController.init(this, callLogger, callGatewayManager);</span>

            // Create the CallerInfoCache singleton, which remembers custom ring tone and
            // send-to-voicemail settings.
            //
            // The asynchronous caching will start just after this call.
            callerInfoCache = CallerInfoCache.init(this);

            // Monitors call activity from the telephony layer
            callStateMonitor = new CallStateMonitor(mCM);

            // Bluetooth manager
            bluetoothManager = new BluetoothManager();

            phoneMgr = PhoneInterfaceManager.init(this, phone);

            // Create the CallNotifer singleton, which handles
            // asynchronous events from the telephony layer (like
            // launching the incoming-call UI when an incoming call comes
            // in.)
           <span style="background-color: rgb(255, 255, 102);"> notifier = CallNotifier.init(this, phone, callLogger, callStateMonitor,
                    bluetoothManager);</span>

            // register for ICC status
            IccCard sim = phone.getIccCard();
            if (sim != null) {
                if (VDBG) Log.v(LOG_TAG, "register for ICC status");
                sim.registerForPersoLocked(mHandler, EVENT_PERSO_LOCKED, null);
            }

            // register for MMI/USSD
            mCM.registerForMmiComplete(mHandler, MMI_COMPLETE, null);

            // register connection tracking to PhoneUtils
            PhoneUtils.initializeConnectionHandler(mCM);

            // Register for misc other intent broadcasts.
            IntentFilter intentFilter =
                    new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
            intentFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
            intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
            intentFilter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
            intentFilter.addAction(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED);
            intentFilter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
            intentFilter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
            intentFilter.addAction(TelephonyIntents.ACTION_MANAGED_ROAMING_IND);
            <span style="background-color: rgb(255, 255, 102);">registerReceiver(mReceiver, intentFilter);</span>

            //set the default values for the preferences in the phone.
            PreferenceManager.setDefaultValues(this, R.xml.network_setting, false);

            PreferenceManager.setDefaultValues(this, R.xml.call_feature_setting, false);

            // Make sure the audio mode (along with some
            // audio-mode-related state of our own) is initialized
            // correctly, given the current state of the phone.
            PhoneUtils.setAudioMode(mCM);
        }

        cdmaOtaProvisionData = new OtaUtils.CdmaOtaProvisionData();
        cdmaOtaConfigData = new OtaUtils.CdmaOtaConfigData();
        cdmaOtaScreenState = new OtaUtils.CdmaOtaScreenState();
        cdmaOtaInCallScreenUiState = new OtaUtils.CdmaOtaInCallScreenUiState();

        // XXX pre-load the SimProvider so that it's ready
        resolver.getType(Uri.parse("content://icc/adn"));

        // start with the default value to set the mute state.
        mShouldRestoreMuteOnInCallResume = false;

        // TODO: Register for Cdma Information Records
        // phone.registerCdmaInformationRecord(mHandler, EVENT_UNSOL_CDMA_INFO_RECORD, null);

        // Read HAC settings and configure audio hardware
        if (getResources().getBoolean(R.bool.hac_enabled)) {
            int hac = android.provider.Settings.System.getInt(phone.getContext().getContentResolver(),
                                                              android.provider.Settings.System.HEARING_AID,
                                                              0);
            AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
            audioManager.setParameter(CallFeaturesSetting.HAC_KEY, hac != 0 ?
                                      CallFeaturesSetting.HAC_VAL_ON :
                                      CallFeaturesSetting.HAC_VAL_OFF);
        }
    }


PhoneFactory.makeDefaultPhones(this); 对framework进行初始化,在frameworks/opt下

    public static void makeDefaultPhone(Context context) {
        synchronized (sLockProxyPhones) {
            if (!sMadeDefaults) {
                sContext = context;

                // create the telephony device controller.
                TelephonyDevController.create();

                int retryCount = 0;
                for(;;) {
                    boolean hasException = false;
                    retryCount ++;

                    try {
                        // use UNIX domain socket to
                        // prevent subsequent initialization
                        new LocalServerSocket("com.android.internal.telephony");
                    } catch (java.io.IOException ex) {
                        hasException = true;
                    }

                    if ( !hasException ) {
                        break;
                    } else if (retryCount > SOCKET_OPEN_MAX_RETRY) {
                        throw new RuntimeException("PhoneFactory probably already running");
                    } else {
                        try {
                            Thread.sleep(SOCKET_OPEN_RETRY_MILLIS);
                        } catch (InterruptedException er) {
                        }
                    }
                }

                sPhoneNotifier = new DefaultPhoneNotifier();

                // Get preferred network mode
                int preferredNetworkMode = RILConstants.PREFERRED_NETWORK_MODE;
                if (TelephonyManager.getLteOnCdmaModeStatic() == PhoneConstants.LTE_ON_CDMA_TRUE) {
                    preferredNetworkMode = Phone.NT_MODE_GLOBAL;
                }

                int cdmaSubscription = CdmaSubscriptionSourceManager.getDefault(context);
                Rlog.i(LOG_TAG, "Cdma Subscription set to " + cdmaSubscription);

                /* In case of multi SIM mode two instances of PhoneProxy, RIL are created,
                   where as in single SIM mode only instance. isMultiSimEnabled() function checks
                   whether it is single SIM or multi SIM mode */
                int numPhones = TelephonyManager.getDefault().getPhoneCount();
                int[] networkModes = new int[numPhones];
                sProxyPhones = new PhoneProxy[numPhones];
                sCommandsInterfaces = new RIL[numPhones];

                for (int i = 0; i < numPhones; i++) {
                    //reads the system properties and makes commandsinterface
                    try {
//                        // Get preferred network type.
//                        TODO: Sishir added this code to but we need a new technique for MSim
//                        int networkType = calculatePreferredNetworkType(context);
//                        Rlog.i(LOG_TAG, "Network Type set to " + Integer.toString(networkType));

                        networkModes[i]  = TelephonyManager.getIntAtIndex(
                                context.getContentResolver(),
                                Settings.Global.PREFERRED_NETWORK_MODE, i);
                    } catch (SettingNotFoundException snfe) {
                        Rlog.e(LOG_TAG, "Settings Exception Reading Value At Index for"+
                                " Settings.Global.PREFERRED_NETWORK_MODE");
                        networkModes[i] = preferredNetworkMode;
                    }

                    if (sContext.getResources().getBoolean(
                            com.android.internal.R.bool.config_global_phone_enabled) &&
                            i == PhoneConstants.PHONE_ID1) {
                        networkModes[i] = Phone.NT_MODE_LTE_CDMA_EVDO_GSM_WCDMA;
                        TelephonyManager.putIntAtIndex( context.getContentResolver(),
                                Settings.Global.PREFERRED_NETWORK_MODE, i, networkModes[i]);
                    }

                    Rlog.i(LOG_TAG, "Network Mode set to " + Integer.toString(networkModes[i]));
                    sCommandsInterfaces[i] = new RIL(context, networkModes[i],
                            cdmaSubscription, i);
                }
                Rlog.i(LOG_TAG, "Creating SubscriptionController");
                SubscriptionController.init(context, sCommandsInterfaces);

                // Instantiate UiccController so that all other classes can just
                // call getInstance()
                mUiccController = UiccController.make(context, sCommandsInterfaces);
                sModemStackController = ModemStackController.make(context,
                        mUiccController, sCommandsInterfaces);
                sModemBindingPolicyHandler = ModemBindingPolicyHandler.make(context,
                        mUiccController, sCommandsInterfaces);

                for (int i = 0; i < numPhones; i++) {
                    PhoneBase phone = null;
                    <span style="background-color: rgb(255, 255, 102);">int phoneType = TelephonyManager.getPhoneType(networkModes[i]);</span>
                    if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
                        phone = new GSMPhone(context,
                                sCommandsInterfaces[i], sPhoneNotifier, i);
                    } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
                        phone = new CDMALTEPhone(context,
                                sCommandsInterfaces[i], sPhoneNotifier, i);
                    }
                    Rlog.i(LOG_TAG, "Creating Phone with type = " + phoneType + " sub = " + i);

                   <span style="background-color: rgb(255, 255, 102);"> sProxyPhones[i] = new PhoneProxy(phone)</span>;
                }
                mProxyController = ProxyController.getInstance(context, sProxyPhones,
                        mUiccController, sCommandsInterfaces);

                // Set the default phone in base class.
                // FIXME: This is a first best guess at what the defaults will be. It
                // FIXME: needs to be done in a more controlled manner in the future.
                sProxyPhone = sProxyPhones[0];
                sCommandsInterface = sCommandsInterfaces[0];

                // Ensure that we have a default SMS app. Requesting the app with
                // updateIfNeeded set to true is enough to configure a default SMS app.
                ComponentName componentName =
                        SmsApplication.getDefaultSmsApplication(context, true /* updateIfNeeded */);
                String packageName = "NONE";
                if (componentName != null) {
                    packageName = componentName.getPackageName();
                }
                Rlog.i(LOG_TAG, "defaultSmsApplication: " + packageName);

                // Set up monitor to watch for changes to SMS packages
                SmsApplication.initSmsPackageMonitor(context);

                sMadeDefaults = true;

                Rlog.i(LOG_TAG, "Creating SubInfoRecordUpdater ");
                sSubInfoRecordUpdater = new SubInfoRecordUpdater(context,
                        sProxyPhones, sCommandsInterfaces);
                SubscriptionController.getInstance().updatePhonesAvailability(sProxyPhones);
            }
        }
    }

这是一个非常重要的方法,创建套接字 通过socket与底层交互,获得卡类型例如当前为GSM,则创建GSMPhone对象

并生成代理对象.存入数组,此方法内对Phone的framework层初始化完毕

3.

<span style="background-color: rgb(255, 255, 51);">phone = PhoneFactory.getDefaultPhone();</span>
<span style="background-color: rgb(255, 255, 51);">
</span>
<span style="background-color: rgb(255, 255, 51);"></span><pre name="code" class="java">    public static Phone getDefaultPhone() {
        synchronized (sLockProxyPhones) {
            if (!sMadeDefaults) {
                throw new IllegalStateException("Default phones haven't been made yet!");
            }
            return sProxyPhone;
        }
    }



sProxyPhone为Phone对象数组中的第0个元素。

4、初始化CallManager (在frameworks/opt下),上下层交互的通道,采用单例模式,非常重要的一个类。

<pre name="code" class="html">            mCM = CallManager.getInstance();
            for (Phone ph : mPhones) {
                mCM.registerPhone(ph);
            }



CallManager的registerPhone是一个非常重要的方法:


    /**
     * Register phone to CallManager
     * @param phone to be registered
     * @return true if register successfully
     */
    public boolean registerPhone(Phone phone) {
        Phone basePhone = getPhoneBase(phone);

        if (basePhone != null && !mPhones.contains(basePhone)) {

            if (DBG) {
                Rlog.d(LOG_TAG, "registerPhone(" +
                        phone.getPhoneName() + " " + phone + ")");
            }

            if (mPhones.isEmpty()) {
                mDefaultPhone = basePhone;
            }
            mPhones.add(basePhone);
            mRingingCalls.add(basePhone.getRingingCall());
            mBackgroundCalls.add(basePhone.getBackgroundCall());
            mForegroundCalls.add(basePhone.getForegroundCall());
            registerForPhoneStates(basePhone);
            return true;
        }
        return false;
    

将当前Phone对应的三个Call对象保存到CallManager的三个对应的数组结构中,并调用私有方法
registerForPhoneStates(basePhone),来注册监听Phone的状态。注意此处传入的是真Phone
<pre name="code" class="java"> private void registerForPhoneStates(Phone phone) {
        // We need to keep a mapping of handler to Phone for proper unregistration.
        // TODO: Clean up this solution as it is just a work around for each Phone instance
        // using the same Handler to register with the RIL. When time permits, we should consider
        // moving the handler (or the reference ot the handler) into the Phone object.
        // See b/17414427.
        CallManagerHandler handler = mHandlerMap.get(phone);
        if (handler != null) {
            Rlog.d(LOG_TAG, "This phone has already been registered.");
            return;
        }

        // New registration, create a new handler instance and register the phone.
        handler = new CallManagerHandler();
        mHandlerMap.put(phone, handler);

        // for common events supported by all phones
        phone.registerForPreciseCallStateChanged(handler, EVENT_PRECISE_CALL_STATE_CHANGED, null);
        phone.registerForDisconnect(handler, EVENT_DISCONNECT, null);
        phone.registerForNewRingingConnection(handler, EVENT_NEW_RINGING_CONNECTION, null);
        phone.registerForUnknownConnection(handler, EVENT_UNKNOWN_CONNECTION, null);
        phone.registerForIncomingRing(handler, EVENT_INCOMING_RING, null);
        phone.registerForRingbackTone(handler, EVENT_RINGBACK_TONE, null);
        phone.registerForInCallVoicePrivacyOn(handler, EVENT_IN_CALL_VOICE_PRIVACY_ON, null);
        phone.registerForInCallVoicePrivacyOff(handler, EVENT_IN_CALL_VOICE_PRIVACY_OFF, null);
        phone.registerForDisplayInfo(handler, EVENT_DISPLAY_INFO, null);
        phone.registerForSignalInfo(handler, EVENT_SIGNAL_INFO, null);
        phone.registerForResendIncallMute(handler, EVENT_RESEND_INCALL_MUTE, null);
        phone.registerForMmiInitiate(handler, EVENT_MMI_INITIATE, null);
        phone.registerForMmiComplete(handler, EVENT_MMI_COMPLETE, null);
        phone.registerForSuppServiceFailed(handler, EVENT_SUPP_SERVICE_FAILED, null);
        phone.registerForServiceStateChanged(handler, EVENT_SERVICE_STATE_CHANGED, null);

        // for events supported only by GSM, CDMA and IMS phone
        if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM ||
                phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA ||
                phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
            phone.setOnPostDialCharacter(handler, EVENT_POST_DIAL_CHARACTER, null);
        }

        // for events supported only by CDMA phone
        if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA ){
            phone.registerForCdmaOtaStatusChange(handler, EVENT_CDMA_OTA_STATUS_CHANGE, null);
            phone.registerForSubscriptionInfoReady(handler, EVENT_SUBSCRIPTION_INFO_READY, null);
            phone.registerForCallWaiting(handler, EVENT_CALL_WAITING, null);
            phone.registerForEcmTimerReset(handler, EVENT_ECM_TIMER_RESET, null);
        }

        // for events supported only by IMS phone
        if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
            phone.registerForOnHoldTone(handler, EVENT_ONHOLD_TONE, null);
        }
    }

剩下的就是初始化一些非常重要的类,和注册广播接收器,就没什么特别的了。

未完待续

<think>嗯,用户的问题是关于在Androidphone进程中如何读取SD卡文件。首先,我需要理解用户的具体需求。用户提到他们之前遇到的问题,可能涉及到进程权限或者存储路径的问题。根据提供的引用,特别是引用[1],用户的service运行在com.android.phone进程,导致访问sdcard目录时权限不同。这可能是因为不同进程有不同的权限配置,或者存储路径访问方式的问题。 接下来,我需要回顾Android的存储系统结构。引用[2]提到不同Android版本的外部存储路径变化,比如sdcard、mnt/sdcard、storage/sdcard等,所以直接使用硬编码路径可能不靠谱。正确的做法应该是使用系统API如Environment.getExternalStorageDirectory()来获取路径,但用户可能需要适配不同版本和厂商的定制路径。 然后,权限问题。引用[2]还指出,从Android 6.0开始需要动态申请权限,所以即使在Manifest中声明了READ_EXTERNAL_STORAGE权限,还需要在运行时请求。不过,如果应用运行在phone进程中,可能需要检查该进程是否有相应的权限。如果phone进程属于系统权限,可能需要系统签名或者更高权限。 另外,引用[3]提到Android启动时的挂载过程,sdcard的owner是system,这可能影响文件访问权限。如果phone进程以system用户运行,可能需要调整文件权限或使用正确的上下文来访问。 用户可能遇到的具体问题是,他们的应用服务运行在phone进程下,无法访问sdcard文件,或者权限不足。解决方案可能包括: 1. 使用正确的系统API获取sdcard路径,而不是硬编码。 2. 确保在Manifest中声明了读写外部存储的权限。 3. 动态请求权限(针对6.0+)。 4. 检查进程的用户权限,可能需要系统签名。 5. 使用ContentResolver或FileProvider来处理文件访问,特别是针对Scoped Storage(Android 10及以上)。 还需要考虑Android版本差异,尤其是Scoped Storage引入后的变化。在Android 10及以上,应用默认只能访问自己的文件和公共目录,可能需要使用MediaStore API来访问其他文件。 此外,引用[4]提到不同应用的Uid问题,但如果是同一应用的不同进程,可能Uid相同,权限共享。但如果phone进程是系统进程,可能需要特殊处理。 总结解决方案步骤:首先确认存储路径的正确获取,检查权限声明和动态申请,处理Android版本差异,考虑进程权限和文件所有者,可能需要系统签名或修改文件权限。</think>在Android系统中,从`phone`进程读取SD卡文件需要特别注意**进程权限**和**存储路径适配性**。以下是分步骤实现方法及技术要点: --- ### 一、权限与进程特性适配 1. **声明基础权限** ```xml <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> ``` - Android 6.0+ 需动态申请权限: ```kotlin ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), REQUEST_CODE) ``` 2. **进程上下文问题** - 若服务运行在`android:process="com.android.phone"`独立进程,需确保该进程具有与应用主进程相同的`sharedUserId`[^4]: ```xml <manifest package="com.example.app" android:sharedUserId="android.uid.phone"> ``` *注意:需系统签名才能使用`android.uid.phone`* --- ### 二、SD卡路径的正确获取 1. **使用系统API替代硬编码路径** ```kotlin val sdcardPath = Environment.getExternalStorageDirectory().absolutePath // 旧版API // 或使用Context方法适配新版本 val filesDir = context.getExternalFilesDir(null)?.path // 私有目录 ``` - Android 4.4+ 推荐通过`Context`获取路径以避免厂商定制差异[^2] 2. **Scoped Storage适配(Android 10+)** - 使用`MediaStore`访问公共文件: ```kotlin val collection = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL) val projection = arrayOf(MediaStore.Images.Media.DATA) val cursor = contentResolver.query(collection, projection, null, null) ``` --- ### 三、Phone进程的特殊处理 1. **系统级文件访问** - 若需访问`/data/data/com.android.phone`目录: ```kotlin val phoneAppFile = File("/data/data/com.android.phone/files/config.xml") ``` *需`android:sharedUserId="android.uid.phone"`且系统签名* 2. **跨进程文件访问** - 通过`FileProvider`共享文件: ```xml <provider android:name="androidx.core.content.FileProvider" android:authorities="com.example.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"/> </provider> ``` --- ### 四、调试与验证 1. **检查文件权限** ```shell adb shell ls -l /sdcard/example.txt # 输出应为:-rw-rw---- 1 root sdcard_rw 1024 ... ``` 2. **验证进程身份** ```kotlin Process.myPid() // 获取当前进程ID Process.myUid() // 检查是否与主进程UID一致 ``` --- ### 典型问题解决方案 | 问题现象 | 解决方法 | |---------------------------|-----------------------------------| | 文件路径不存在 | 使用`Environment.getExternalStorageState()`检查挂载状态 | | Permission denied | 检查进程UID一致性或添加`android:requestLegacyExternalStorage="true"` | | 系统签名缺失 | 使用平台签名密钥编译APK | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值