• Andorid11系统自带Dialer不是默认应用


    项目中系统自带Dialer应用不是默认应用,导致输入暗码不能启动工程模式。

    1.加载默认支持的role

    RoleManagert和RoleManagerService主要用来管理默认应用设置的,RoleManagerService继承SystemService,在启动的时候开始加载系统默认支持的role。

    • frameworks/base/services/core/java/com/android/server/role/RoleManagerService.java
    	@Override
        public void onStart() {
        	publishBinderService(Context.ROLE_SERVICE, new Stub());
    
            IntentFilter intentFilter = new IntentFilter();
            intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
            intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
            intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
            intentFilter.addDataScheme("package");
            intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
            getContext().registerReceiverAsUser(new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    int userId = UserHandle.getUserId(intent.getIntExtra(Intent.EXTRA_UID, -1));
                    if (DEBUG) {
                        Slog.i(LOG_TAG, "Packages changed - re-running initial grants for user "
                                + userId);
                    }
                    if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())
                            && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
                        // Package is being upgraded - we're about to get ACTION_PACKAGE_ADDED
                        return;
                    }
                    maybeGrantDefaultRolesAsync(userId);
                }
            }, UserHandle.ALL, intentFilter, null, null);
        }
        
        private void maybeGrantDefaultRolesAsync(@UserIdInt int userId) {
            ThrottledRunnable runnable;
            synchronized (mLock) {
                runnable = mGrantDefaultRolesThrottledRunnables.get(userId);
                if (runnable == null) {
                    runnable = new ThrottledRunnable(FgThread.getHandler(),
                            GRANT_DEFAULT_ROLES_INTERVAL_MILLIS,
                            () -> maybeGrantDefaultRolesInternal(userId));
                    mGrantDefaultRolesThrottledRunnables.put(userId, runnable);
                }
            }
            runnable.run();
        }
        
         // 加载默认支持的role
        @AnyThread
        @NonNull
        private AndroidFuture<Void> maybeGrantDefaultRolesInternal(@UserIdInt int userId) {
        	// 根据userId创建RoleUserState,在RoleUserState中解析roles.xml,但是framework下没有roles.xml文件
            RoleUserState userState = getOrCreateUserState(userId);
            String oldPackagesHash = userState.getPackagesHash();
            String newPackagesHash = computeComponentStateHash(userId);
            if (Objects.equals(oldPackagesHash, newPackagesHash)) {
                if (DEBUG) {
                    Slog.i(LOG_TAG, "Already granted default roles for packages hash "
                            + newPackagesHash);
                }
                return AndroidFuture.completedFuture(null);
            }
    
            //TODO gradually add more role migrations statements here for remaining roles
            // Make sure to implement LegacyRoleResolutionPolicy#getRoleHolders
            // for a given role before adding a migration statement for it here
            // roles.xml中还有两种
            aybeMigrateRole(RoleManager.ROLE_ASSISTANT, userId);
            maybeMigrateRole(RoleManager.ROLE_BROWSER, userId);
            maybeMigrateRole(RoleManager.ROLE_DIALER, userId);
            maybeMigrateRole(RoleManager.ROLE_SMS, userId);
            maybeMigrateRole(RoleManager.ROLE_EMERGENCY, userId);
            maybeMigrateRole(RoleManager.ROLE_HOME, userId);
            
            // Some package state has changed, so grant default roles again.
            Slog.i(LOG_TAG, "Granting default roles...");
            AndroidFuture<Void> future = new AndroidFuture<>();
            
            //在RoleControllerManager中授予默认role
            getOrCreateController(userId).grantDefaultRoles(FgThread.getExecutor(),
                    successful -> {
                        if (successful) {
                            userState.setPackagesHash(newPackagesHash);
                            future.complete(null);
                        } else {
                            future.completeExceptionally(new RuntimeException());
                        }
                    });
            return future;
        }  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85

    走到RoleControllerManager.grantDefaultRoles中

    • frameworks/base/core/java/android/app/role/RoleControllerManager.java
    	/**
         * @see RoleControllerService#onGrantDefaultRoles()
         *
         * @hide
         */
        public void grantDefaultRoles(@NonNull @CallbackExecutor Executor executor,
                @NonNull Consumer<Boolean> callback) {
            AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
                AndroidFuture<Bundle> future = new AndroidFuture<>();
                service.grantDefaultRoles(new RemoteCallback(future::complete));
                return future;
            });
            propagateCallback(operation, "grantDefaultRoles", executor, callback);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    然后再到RoleControllerService.grantDefaultRoles中

    • frameworks/base/core/java/android/app/role/RoleControllerService.java
    @SystemApi
    public abstract class RoleControllerService extends Service {
    		
    	@Nullable
        @Override
        public final IBinder onBind(@Nullable Intent intent) {
            return new IRoleController.Stub() {
    
                @Override
                public void grantDefaultRoles(RemoteCallback callback) {
                    enforceCallerSystemUid("grantDefaultRoles");
    
                    Objects.requireNonNull(callback, "callback cannot be null");
    
                    mWorkerHandler.sendMessage(PooledLambda.obtainMessage(
                            RoleControllerService::grantDefaultRoles, RoleControllerService.this,
                            callback));
                }
                ...
            )};
         }     
         
         private void grantDefaultRoles(@NonNull RemoteCallback callback) {
            boolean successful = onGrantDefaultRoles();
            callback.sendResult(successful ? Bundle.EMPTY : null);
        }
        
        @WorkerThread
        public abstract boolean onGrantDefaultRoles();
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    RoleControllerService是一个抽象类,它还有一个子类RoleControllerServiceImpl,然后走到RoleControllerServiceImpl.onGrantDefaultRoles方法

    • packages/apps/PermissionController/src/com/android/permissioncontroller/role/service/RoleControllerServiceImpl.java
    	@Override
    	@WorkerThread
    	public boolean onGrantDefaultRoles() {
            if (DEBUG) {
                Log.i(LOG_TAG, "Granting default roles, user: " + UserHandle.myUserId());
            }
            //第一步: 解析roles.xml获取所有可用的roles
            // Gather the available roles for current user.
            ArrayMap<String, Role> roleMap = Roles.get(this);
            List<Role> roles = new ArrayList<>();
            List<String> roleNames = new ArrayList<>();
            ArraySet<String> addedRoleNames = new ArraySet<>();
            int roleMapSize = roleMap.size();
            for (int i = 0; i < roleMapSize; i++) {
                Role role = roleMap.valueAt(i);
                
                // 第二步:判断role是否可用
                if (!role.isAvailable(this)) {
                    continue;
                }
                roles.add(role);
                String roleName = role.getName();
                roleNames.add(roleName);
                if (!mRoleManager.isRoleAvailable(roleName)) {
                    addedRoleNames.add(roleName);
                }
            }
            
            // TODO: Clean up holders of roles that will be removed.
            
            // 第三步:将所有可用的rols设置到RoleManager
            // Set the available role names in RoleManager.
            mRoleManager.setRoleNamesFromController(roleNames);
            ...
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    第一步解析roles.xml
    onGrantDefaultRoles调用后马上去读取xml–>Roles.get(this)

    • packages/apps/PermissionController/src/com/android/permissioncontroller/role/model/Roles.java
     	@NonNull
        public static ArrayMap<String, Role> get(@NonNull Context context) {
            synchronized (sLock) {
                if (sRoles == null) {
                    sRoles = new RoleParser(context).parse();
                }
                return sRoles;
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在RoleParser.java中进行roles.xml的解析

    • packages/apps/PermissionController/src/com/android/permissioncontroller/role/model/RoleParser.java
    	@NonNull
        public ArrayMap<String, Role> parse() {
            try (XmlResourceParser parser = mContext.getResources().getXml(R.xml.roles)) {
                Pair<ArrayMap<String, PermissionSet>, ArrayMap<String, Role>> xml = parseXml(parser);
                if (xml == null) {
                    return new ArrayMap<>();
                }
                ArrayMap<String, PermissionSet> permissionSets = xml.first;
                ArrayMap<String, Role> roles = xml.second;
                validateResult(permissionSets, roles);
                return roles;
            } catch (XmlPullParserException | IOException e) {
                throwOrLogMessage("Unable to parse roles.xml", e);
                return new ArrayMap<>();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    第二步中判断role是否可用
    role.isAvailable(this),如果可用进行第三步

    • packages/apps/PermissionController/src/com/android/permissioncontroller/role/model/Role.java
    	public boolean isAvailable(@NonNull Context context) {
            return isAvailableAsUser(Process.myUserHandle(), context);
        }
        ...
        public boolean isAvailableAsUser(@NonNull UserHandle user, @NonNull Context context) {
            if (mBehavior != null) {
                return mBehavior.isAvailableAsUser(this, user, context);
            }
            return true;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    RoleBehavior是一个接口类,在RoleBehavior.isVisibleAsUser中默认返回true

    第三步添加可用role到RoleManager
    从RoleManager.setRoleNamesFromController再到RoleManagerService.setRoleNamesFromController

    • frameworks/base/services/core/java/com/android/server/role/RoleManagerService.java
    	@Override
        public void setRoleNamesFromController(@NonNull List<String> roleNames) {
            getContext().enforceCallingOrSelfPermission(
                        RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER,
                        "setRoleNamesFromController");
    
            Objects.requireNonNull(roleNames, "roleNames cannot be null");
    
            int userId = UserHandle.getCallingUserId();
            getOrCreateUserState(userId).setRoleNames(roleNames);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    又来到RoleUserState.setRoleNames

    • frameworks/base/services/core/java/com/android/server/role/RoleUserState.java
    /**
         * Set the names of all available roles.
         *
         * @param roleNames the names of all the available roles
         */
        public void setRoleNames(@NonNull List<String> roleNames) {
            synchronized (mLock) {
                boolean changed = false;
    
                for (int i = mRoles.size() - 1; i >= 0; i--) {
                    String roleName = mRoles.keyAt(i);
    
                    if (!roleNames.contains(roleName)) {
                        ArraySet<String> packageNames = mRoles.valueAt(i);
                        if (!packageNames.isEmpty()) {
                            Slog.e(LOG_TAG, "Holders of a removed role should have been cleaned up,"
                                    + " role: " + roleName + ", holders: " + packageNames);
                        }
                        mRoles.removeAt(i);
                        changed = true;
                    }
                }
                int roleNamesSize = roleNames.size();
                for (int i = 0; i < roleNamesSize; i++) {
                    changed |= addRoleName(roleNames.get(i));
                }
    
                if (changed) {
                   // 保存在本地文件
                    scheduleWriteFileLocked();
                }
            }
        }
        ...
        /**
         * Adds the given role, effectively marking it as {@link #isRoleAvailable available}
         *
         * @param roleName the name of the role
         *
         * @return whether any changes were made
         */
        public boolean addRoleName(@NonNull String roleName) {
            synchronized (mLock) {
                if (!mRoles.containsKey(roleName)) {
                    // 向mRoles中添加数据
                    mRoles.put(roleName, new ArraySet<>());
                    Slog.i(LOG_TAG, "Added new role: " + roleName);
                    scheduleWriteFileLocked();
                    return true;
                } else {
                    return false;
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    开机加载默认支持的role基本就是这样了

    2.判断是否是Default Dialer

    暗码启动(一)中有提到,输入暗码后会对版本和是否是默认Dialer应用进行判断

    • packages/apps/Dialer/java/com/android/dialer/compat/telephony/TelephonyManagerCompat.java
    public static void handleSecretCode(Context context, String secretCode) {
    
        // Must use system service on O+ to avoid using broadcasts, which are not allowed on O+.
        if (BuildCompat.isAtLeastO()) {
          if (!TelecomUtil.isDefaultDialer(context)) {
            LogUtil.e(
                "TelephonyManagerCompat.handleSecretCode",
                "not default dialer, cannot send special code");
            return;
          }
          context.getSystemService(TelephonyManager.class).sendDialerSpecialCode(secretCode);
        } else {
          // System service call is not supported pre-O, so must use a broadcast for N-.
          Intent intent =
              new Intent(SECRET_CODE_ACTION, Uri.parse("android_secret_code://" + secretCode));
          context.sendBroadcast(intent);
        }
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    如果不是默认的Dialer应用直接返回不处理,抓取的log中也有not default dialer字样,所以说当前使用的Dialer应用不是默认的,就没有继续往下走。
    看下是怎么判定default dialer,追踪流程如下:

    • packages/apps/Dialer/java/com/android/dialer/telecom/TelecomUtil.java
    public boolean isDefaultDialer(Context context) {
          final boolean result =
              TextUtils.equals(
                  context.getPackageName(), getTelecomManager(context).getDefaultDialerPackage());
          if (result) {
            warningLogged = false;
          } else {
            if (!warningLogged) {
              // Log only once to prevent spam.
              LogUtil.w(TAG, "Dialer is not currently set to be default dialer");
              warningLogged = true;
            }
          }
          return result;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    从TelecomManager中获取getDefaultDialerPackage(),然后又调用到TelecomServiceImpl中的getDefaultDialerPackage()。

    • packages/services/Telecomm/src/com/android/server/telecom/TelecomServiceImpl.java
    @Override
            public String getDefaultDialerPackage() {
                try {
                    Log.startSession("TSI.gDDP");
                    final long token = Binder.clearCallingIdentity();
                    try {
                        return mDefaultDialerCache.getDefaultDialerApplication(
                                ActivityManager.getCurrentUser());
                    } finally {
                        Binder.restoreCallingIdentity(token);
                    }
                } finally {
                    Log.endSession();
                }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • packages/services/Telecomm/src/com/android/server/telecom/DefaultDialerCache.java
    	public String getDefaultDialerApplication(int userId) {
            if (userId == UserHandle.USER_CURRENT) {
                userId = ActivityManager.getCurrentUser();
            }
    
            if (userId < 0) {
                Log.w(LOG_TAG, "Attempting to get default dialer for a meta-user %d", userId);
                return null;
            }
    
            // TODO: Re-enable this when we are able to use the cache once more.  RoleManager does not
            // provide a means for being informed when the role holder changes at the current time.
            //
            //synchronized (mLock) {
            //    String defaultDialer = mCurrentDefaultDialerPerUser.get(userId);
            //    if (defaultDialer != null) {
            //        return defaultDialer;
            //    }
            //}
            return refreshCacheForUser(userId);
        }
        
    	...
    	
    	private String refreshCacheForUser(int userId) {;
            String currentDefaultDialer =
                    mRoleManagerAdapter.getDefaultDialerApp(userId);
            synchronized (mLock) {
                mCurrentDefaultDialerPerUser.put(userId, currentDefaultDialer);
            }
            return currentDefaultDialer;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    RoleManagerAdapter是一个接口类,具体方法实现在RoleManagerAdapterImpl中

    • packages/services/Telecomm/src/com/android/server/telecom/RoleManagerAdapterImpl.java
     	@Override
        public String getDefaultDialerApp(int user) {
            if (mOverrideDefaultDialerApp != null) {
                return mOverrideDefaultDialerApp;
            }
            return getRoleManagerDefaultDialerApp(user);
        }
        
        ...
        
        // 通过ROLE_DIALER获取默认Dialer应用包名;
        private String getRoleManagerDefaultDialerApp(int user) {
            List<String> roleHolders = mRoleManager.getRoleHoldersAsUser(ROLE_DIALER,
                    new UserHandle(user));
            if (roleHolders == null || roleHolders.isEmpty()) {
                return null;
            }
            return roleHolders.get(0);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    从这里可以看出是从RoleManager中获取名称为ROLE_DIALER的dialer应用包名。
    ROLE_DIALER = android.app.role.DIALER
    又从RoleManager的getRoleHoldersAsUser()方法,调用到服务RoleManagerService中的getRoleHoldersAsUser()

    • frameworks/base/services/core/java/com/android/server/role/RoleManagerService.java
     @NonNull
     @Override
     public List<String> getRoleHoldersAsUser(@NonNull String roleName, @UserIdInt int userId) {
         if (!mUserManagerInternal.exists(userId)) {
             Slog.e(LOG_TAG, "user " + userId + " does not exist");
             return Collections.emptyList();
         }
    
         userId = handleIncomingUser(userId, false, "getRoleHoldersAsUser");
         getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
                        "getRoleHoldersAsUser");
    
         Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
         
         ArraySet<String> roleHolders = getOrCreateUserState(userId).getRoleHolders(roleName);
         if (roleHolders == null) {
             return Collections.emptyList();
         }
         return new ArrayList<>(roleHolders);
     }
     
     ...
    
     @NonNull
     private RoleUserState getOrCreateUserState(@UserIdInt int userId) {
            synchronized (mLock) {
                RoleUserState userState = mUserStates.get(userId);
                if (userState == null) {
                    userState = new RoleUserState(userId, this);
                    mUserStates.put(userId, userState);
                }
                return userState;
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • frameworks/base/services/core/java/com/android/server/role/RoleUserState.java
    	@Nullable
        public ArraySet<String> getRoleHolders(@NonNull String roleName) {
            synchronized (mLock) {
                // 通过传过来的android.app.role.DIALER获取包名
                ArraySet<String> packageNames = mRoles.get(roleName);
                if (packageNames == null) {
                    return null;
                }
                return new ArraySet<>(packageNames);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这个mRoles就是最开始通过解析xml得到的,包含所有可用的role。但是这里返回为null,说明mRoles中没有这个role,android.app.role.DIALER不可用了。

    roles.xml中Dialer相关代码

    • packages/apps/PermissionController/res/xml/roles.xml
     <role
            name="android.app.role.DIALER"
            behavior="DialerRoleBehavior" // Dialer应用使用
            defaultHolders="config_defaultDialer"
            description="@string/role_dialer_description"
            exclusive="true"
            fallBackToDefaultHolder="true"
            label="@string/role_dialer_label"
            requestDescription="@string/role_dialer_request_description"
            requestTitle="@string/role_dialer_request_title"
            searchKeywords="@string/role_dialer_search_keywords"
            shortLabel="@string/role_dialer_short_label">
            <required-components>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    再看DialerRoleBehavior继承自RoleBehavior

    • packages/apps/PermissionController/src/com/android/permissioncontroller/role/model/DialerRoleBehavior.java
     @Override
        public boolean isAvailableAsUser(@NonNull Role role, @NonNull UserHandle user,
                @NonNull Context context) {
            TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
            return telephonyManager.isVoiceCapable();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • frameworks/base/telephony/java/android/telephony/TelephonyManager.java
    	public boolean isVoiceCapable() {
            if (mContext == null) return true;
            return mContext.getResources().getBoolean(
                    com.android.internal.R.bool.config_voice_capable);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    看到这里找到原因了,我们的项目不支持通话,把config_voice_capable设为false了,所以导致RoleManager.ROLE_DIALER = android.app.role.DIALER这个role不可用。

  • 相关阅读:
    GitHub上250K Stars阿里首发Java并发编程
    pycharm使用
    Kafka -- 架构、分区、副本
    程序进程和线程(线程的并发与并行)以及线程的基本创建和使用
    【无标Spring Boot (十五): Spring Boot + Jpa + Thymeleaf 增删改查示例题】
    记docker部署logstash的一次报错
    nvidia-smi常用选项汇总
    BGP高级特性——BGP路由控制
    【react native】模拟mock接口
    科研试剂DMPE-PEG-Mal 二肉豆蔻酰磷脂酰乙醇胺-聚乙二醇-马来酰亚胺
  • 原文地址:https://blog.csdn.net/wxd_csdn_2016/article/details/127871366