• 探索 Android的SSAID(Android ID) 的奥秘


    本篇文章基 android-11.0.0_r17 编写

    我们在做App开发的时候,通常会有获取唯一标示的需求,在这里Android提供了ANDROID_ID的方式来满足大家需求

    
    String androidId = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
    
    

    我可以通过Settings下的Secure内部类下的getString方法来获取,并且传入了一个 ContentResolver 对象以及系统提供的 Settings.Secure.ANDROID_ID 常量值,看到这里大家不妨猜一下,我想这里肯定是通过IPC的方式访问Settings应用,然后根据自身的应用信息来去生成一个ID,我们继续往下

    
    public static String getString(ContentResolver resolver, String name) {
    
        return getStringForUser(resolver, name, resolver.getUserId());
    
    }
    
    public static String getStringForUser(ContentResolver resolver, String name,
    
            int userHandle) {
    
        if (MOVED_TO_GLOBAL.contains(name)) {
    
            Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.Secure"
    
                    + " to android.provider.Settings.Global.");
    
            return Global.getStringForUser(resolver, name, userHandle);
    
        }
    
        if (MOVED_TO_LOCK_SETTINGS.contains(name)) {
    
            synchronized (Secure.class) {
    
                if (sLockSettings == null) {
    
                    sLockSettings = ILockSettings.Stub.asInterface(
    
                            (IBinder) ServiceManager.getService("lock_settings"));
    
                    sIsSystemProcess = Process.myUid() == Process.SYSTEM_UID;
    
                }
    
            }
    
            if (sLockSettings != null && !sIsSystemProcess) {
    
                // No context; use the ActivityThread's context as an approximation for
    
                // determining the target API level.
    
                Application application = ActivityThread.currentApplication();
    
                boolean isPreMnc = application != null
    
                        && application.getApplicationInfo() != null
    
                        && application.getApplicationInfo().targetSdkVersion
    
                        <= VERSION_CODES.LOLLIPOP_MR1;
    
                if (isPreMnc) {
    
                    try {
    
                        return sLockSettings.getString(name, "0", userHandle);
    
                    } catch (RemoteException re) {
    
                        // Fall through
    
                    }
    
                } else {
    
                    throw new SecurityException("Settings.Secure." + name
    
                            + " is deprecated and no longer accessible."
    
                            + " See API documentation for potential replacements.");
    
                }
    
            }
    
        }
    
        return sNameValueCache.getStringForUser(resolver, name, userHandle);
    
    }
    
    

    首先判断两个List里是否存在name,这里如果name=ANDROID_ID的话,两个if都不会命中,会直接从最后行来获取,这里可能是缓存,我们先进去看看具体逻辑

    
    @UnsupportedAppUsage
    
    public String getStringForUser(ContentResolver cr, String name, final int userHandle) {
    
        //首先判断当前应用是不是属于当前登录用户
    
        final boolean isSelf = (userHandle == UserHandle.myUserId());
    
        int currentGeneration = -1;
    
        if (isSelf) {
    
            synchronized (NameValueCache.this) {
    
                if (mGenerationTracker != null) {
    
                    if (mGenerationTracker.isGenerationChanged()) {
    
                        if (DEBUG) {
    
                            Log.i(TAG, "Generation changed for type:"
    
                                    + mUri.getPath() + " in package:"
    
                                    + cr.getPackageName() +" and user:" + userHandle);
    
                        }
    
                        mValues.clear();
    
                    } else if (mValues.containsKey(name)) {
    
                        return mValues.get(name);
    
                    }
    
                    if (mGenerationTracker != null) {
    
                        currentGeneration = mGenerationTracker.getCurrentGeneration();
    
                    }
    
                }
    
            }
    
        } else {
    
            if (LOCAL_LOGV) Log.v(TAG, "get setting for user " + userHandle
    
                    + " by user " + UserHandle.myUserId() + " so skipping cache");
    
        }
    
        IContentProvider cp = mProviderHolder.getProvider(cr);
    
        // Try the fast path first, not using query().  If this
    
        // fails (alternate Settings provider that doesn't support
    
        // this interface?) then we fall back to the query/table
    
        // interface.
    
        if (mCallGetCommand != null) {
    
            try {
    
                Bundle args = null;
    
                if (!isSelf) {
    
                    args = new Bundle();
    
                    args.putInt(CALL_METHOD_USER_KEY, userHandle);
    
                }
    
                boolean needsGenerationTracker = false;
    
                synchronized (NameValueCache.this) {
    
                    if (isSelf && mGenerationTracker == null) {
    
                        needsGenerationTracker = true;
    
                        if (args == null) {
    
                            args = new Bundle();
    
                        }
    
                        args.putString(CALL_METHOD_TRACK_GENERATION_KEY, null);
    
                        if (DEBUG) {
    
                            Log.i(TAG, "Requested generation tracker for type: "+ mUri.getPath()
    
                                    + " in package:" + cr.getPackageName() +" and user:"
    
                                    + userHandle);
    
                        }
    
                    }
    
                }
    
                Bundle b;
    
                // If we're in system server and in a binder transaction we need to clear the
    
                // calling uid. This works around code in system server that did not call
    
                // clearCallingIdentity, previously this wasn't needed because reading settings
    
                // did not do permission checking but thats no longer the case.
    
                // Long term this should be removed and callers should properly call
    
                // clearCallingIdentity or use a ContentResolver from the caller as needed.
    
                if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) {
    
                    final long token = Binder.clearCallingIdentity();
    
                    try {
    
                        b = cp.call(cr.getPackageName(), cr.getAttributionTag(),
    
                                mProviderHolder.mUri.getAuthority(), mCallGetCommand, name,
    
                                args);
    
                    } finally {
    
                        Binder.restoreCallingIdentity(token);
    
                    }
    
                } else {
    
                    b = cp.call(cr.getPackageName(), cr.getAttributionTag(),
    
                            mProviderHolder.mUri.getAuthority(), mCallGetCommand, name, args);
    
                }
    
                if (b != null) {
    
                    String value = b.getString(Settings.NameValueTable.VALUE);
    
                    // Don't update our cache for reads of other users' data
    
                    if (isSelf) {
    
                        synchronized (NameValueCache.this) {
    
                            if (needsGenerationTracker) {
    
                                MemoryIntArray array = b.getParcelable(
    
                                        CALL_METHOD_TRACK_GENERATION_KEY);
    
                                final int index = b.getInt(
    
                                        CALL_METHOD_GENERATION_INDEX_KEY, -1);
    
                                if (array != null && index >= 0) {
    
                                    final int generation = b.getInt(
    
                                            CALL_METHOD_GENERATION_KEY, 0);
    
                                    if (DEBUG) {
    
                                        Log.i(TAG, "Received generation tracker for type:"
    
                                                + mUri.getPath() + " in package:"
    
                                                + cr.getPackageName() + " and user:"
    
                                                + userHandle + " with index:" + index);
    
                                    }
    
                                    if (mGenerationTracker != null) {
    
                                        mGenerationTracker.destroy();
    
                                    }
    
                                    mGenerationTracker = new GenerationTracker(array, index,
    
                                            generation, () -> {
    
                                        synchronized (NameValueCache.this) {
    
                                            Log.e(TAG, "Error accessing generation"
    
                                                    + " tracker - removing");
    
                                            if (mGenerationTracker != null) {
    
                                                GenerationTracker generationTracker =
    
                                                        mGenerationTracker;
    
                                                mGenerationTracker = null;
    
                                                generationTracker.destroy();
    
                                                mValues.clear();
    
                                            }
    
                                        }
    
                                    });
    
                                    currentGeneration = generation;
    
                                }
    
                            }
    
                            if (mGenerationTracker != null && currentGeneration ==
    
                                    mGenerationTracker.getCurrentGeneration()) {
    
                                mValues.put(name, value);
    
                            }
    
                        }
    
                    } else {
    
                        if (LOCAL_LOGV) Log.i(TAG, "call-query of user " + userHandle
    
                                + " by " + UserHandle.myUserId()
    
                                + " so not updating cache");
    
                    }
    
                    return value;
    
                }
    
                // If the response Bundle is null, we fall through
    
                // to the query interface below.
    
            } catch (RemoteException e) {
    
                // Not supported by the remote side?  Fall through
    
                // to query().
    
            }
    
        }
    
        Cursor c = null;
    
        try {
    
            Bundle queryArgs = ContentResolver.createSqlQueryBundle(
    
                    NAME_EQ_PLACEHOLDER, new String[]{name}, null);
    
            // Same workaround as above.
    
            if (Settings.isInSystemServer() && Binder.getCallingUid() != Process.myUid()) {
    
                final long token = Binder.clearCallingIdentity();
    
                try {
    
                    c = cp.query(cr.getPackageName(), cr.getAttributionTag(), mUri,
    
                            SELECT_VALUE_PROJECTION, queryArgs, null);
    
                } finally {
    
                    Binder.restoreCallingIdentity(token);
    
                }
    
            } else {
    
                c = cp.query(cr.getPackageName(), cr.getAttributionTag(), mUri,
    
                        SELECT_VALUE_PROJECTION, queryArgs, null);
    
            }
    
            if (c == null) {
    
                Log.w(TAG, "Can't get key " + name + " from " + mUri);
    
                return null;
    
            }
    
            String value = c.moveToNext() ? c.getString(0) : null;
    
            synchronized (NameValueCache.this) {
    
                if (mGenerationTracker != null
    
                        && currentGeneration == mGenerationTracker.getCurrentGeneration()) {
    
                    mValues.put(name, value);
    
                }
    
            }
    
            if (LOCAL_LOGV) {
    
                Log.v(TAG, "cache miss [" + mUri.getLastPathSegment() + "]: " +
    
                        name + " = " + (value == null ? "(null)" : value));
    
            }
    
            return value;
    
        } catch (RemoteException e) {
    
            Log.w(TAG, "Can't get key " + name + " from " + mUri, e);
    
            return null;  // Return null, but don't cache it.
    
        } finally {
    
            if (c != null) c.close();
    
        }
    
    }
    
    

    别看这个方法这么多代码,但是其实真正核心的逻辑只有两行,得到SettingsProvider的ContentProvider

    然后在调用call方法来查询数据,返回一个Bundle数据,我们的AndroidID就在这个Bundle中,我们先去看这个Provider的实现

    
    IContentProvider cp = mProviderHolder.getProvider(cr);
    
    b = cp.call(cr.getPackageName(), cr.getAttributionTag(),
    
                    mProviderHolder.mUri.getAuthority(), mCallGetCommand, name, args);
    
    

    我们来到了SettingsProvider这个项目下,找到对应的Provider,代码如下

    
    @Override
    
    public Bundle call(String method, String name, Bundle args) {
    
        final int requestingUserId = getRequestingUserId(args);
    
        switch (method) {
    
            case Settings.CALL_METHOD_GET_CONFIG: {
    
                Setting setting = getConfigSetting(name);
    
                return packageValueForCallResult(setting, isTrackingGeneration(args));
    
            }
    
            case Settings.CALL_METHOD_GET_GLOBAL: {
    
                Setting setting = getGlobalSetting(name);
    
                return packageValueForCallResult(setting, isTrackingGeneration(args));
    
            }
    
            case Settings.CALL_METHOD_GET_SECURE: {
    
                Setting setting = getSecureSetting(name, requestingUserId,
    
                        /*enableOverride=*/ true);
    
                return packageValueForCallResult(setting, isTrackingGeneration(args));
    
            }
    
            case Settings.CALL_METHOD_GET_SYSTEM: {
    
                Setting setting = getSystemSetting(name, requestingUserId);
    
                return packageValueForCallResult(setting, isTrackingGeneration(args));
    
            }
    
            case Settings.CALL_METHOD_PUT_CONFIG: {
    
                String value = getSettingValue(args);
    
                final boolean makeDefault = getSettingMakeDefault(args);
    
                insertConfigSetting(name, value, makeDefault);
    
                break;
    
            }
    
            case Settings.CALL_METHOD_PUT_GLOBAL: {
    
                String value = getSettingValue(args);
    
                String tag = getSettingTag(args);
    
                final boolean makeDefault = getSettingMakeDefault(args);
    
                final boolean overrideableByRestore = getSettingOverrideableByRestore(args);
    
                insertGlobalSetting(name, value, tag, makeDefault, requestingUserId, false,
    
                        overrideableByRestore);
    
                break;
    
            }
    
            case Settings.CALL_METHOD_PUT_SECURE: {
    
                String value = getSettingValue(args);
    
                String tag = getSettingTag(args);
    
                final boolean makeDefault = getSettingMakeDefault(args);
    
                final boolean overrideableByRestore = getSettingOverrideableByRestore(args);
    
                insertSecureSetting(name, value, tag, makeDefault, requestingUserId, false,
    
                        overrideableByRestore);
    
                break;
    
            }
    
            case Settings.CALL_METHOD_PUT_SYSTEM: {
    
                String value = getSettingValue(args);
    
                boolean overrideableByRestore = getSettingOverrideableByRestore(args);
    
                insertSystemSetting(name, value, requestingUserId, overrideableByRestore);
    
                break;
    
            }
    
            case Settings.CALL_METHOD_SET_ALL_CONFIG: {
    
                String prefix = getSettingPrefix(args);
    
                Map<String, String> flags = getSettingFlags(args);
    
                Bundle result = new Bundle();
    
                result.putBoolean(Settings.KEY_CONFIG_SET_RETURN,
    
                        setAllConfigSettings(prefix, flags));
    
                return result;
    
            }
    
            case Settings.CALL_METHOD_RESET_CONFIG: {
    
                final int mode = getResetModeEnforcingPermission(args);
    
                String prefix = getSettingPrefix(args);
    
                resetConfigSetting(mode, prefix);
    
                break;
    
            }
    
            case Settings.CALL_METHOD_RESET_GLOBAL: {
    
                final int mode = getResetModeEnforcingPermission(args);
    
                String tag = getSettingTag(args);
    
                resetGlobalSetting(requestingUserId, mode, tag);
    
                break;
    
            }
    
            case Settings.CALL_METHOD_RESET_SECURE: {
    
                final int mode = getResetModeEnforcingPermission(args);
    
                String tag = getSettingTag(args);
    
                resetSecureSetting(requestingUserId, mode, tag);
    
                break;
    
            }
    
            case Settings.CALL_METHOD_DELETE_CONFIG: {
    
                int rows  = deleteConfigSetting(name) ? 1 : 0;
    
                Bundle result = new Bundle();
    
                result.putInt(RESULT_ROWS_DELETED, rows);
    
                return result;
    
            }
    
            case Settings.CALL_METHOD_DELETE_GLOBAL: {
    
                int rows = deleteGlobalSetting(name, requestingUserId, false) ? 1 : 0;
    
                Bundle result = new Bundle();
    
                result.putInt(RESULT_ROWS_DELETED, rows);
    
                return result;
    
            }
    
            case Settings.CALL_METHOD_DELETE_SECURE: {
    
                int rows = deleteSecureSetting(name, requestingUserId, false) ? 1 : 0;
    
                Bundle result = new Bundle();
    
                result.putInt(RESULT_ROWS_DELETED, rows);
    
                return result;
    
            }
    
            case Settings.CALL_METHOD_DELETE_SYSTEM: {
    
                int rows = deleteSystemSetting(name, requestingUserId) ? 1 : 0;
    
                Bundle result = new Bundle();
    
                result.putInt(RESULT_ROWS_DELETED, rows);
    
                return result;
    
            }
    
            case Settings.CALL_METHOD_LIST_CONFIG: {
    
                String prefix = getSettingPrefix(args);
    
                Bundle result = packageValuesForCallResult(getAllConfigFlags(prefix),
    
                        isTrackingGeneration(args));
    
                reportDeviceConfigAccess(prefix);
    
                return result;
    
            }
    
            case Settings.CALL_METHOD_REGISTER_MONITOR_CALLBACK_CONFIG: {
    
                RemoteCallback callback = args.getParcelable(
    
                        Settings.CALL_METHOD_MONITOR_CALLBACK_KEY);
    
                setMonitorCallback(callback);
    
                break;
    
            }
    
            case Settings.CALL_METHOD_LIST_GLOBAL: {
    
                Bundle result = new Bundle();
    
                result.putStringArrayList(RESULT_SETTINGS_LIST,
    
                        buildSettingsList(getAllGlobalSettings(null)));
    
                return result;
    
            }
    
            case Settings.CALL_METHOD_LIST_SECURE: {
    
                Bundle result = new Bundle();
    
                result.putStringArrayList(RESULT_SETTINGS_LIST,
    
                        buildSettingsList(getAllSecureSettings(requestingUserId, null)));
    
                return result;
    
            }
    
            case Settings.CALL_METHOD_LIST_SYSTEM: {
    
                Bundle result = new Bundle();
    
                result.putStringArrayList(RESULT_SETTINGS_LIST,
    
                        buildSettingsList(getAllSystemSettings(requestingUserId, null)));
    
                return result;
    
            }
    
            default: {
    
                Slog.w(LOG_TAG, "call() with invalid method: " + method);
    
            } break;
    
        }
    
        return null;
    
    }
    
    

    这里呢,我们只关心 CALL_METHOD_GET_SECURE 。为了代码的完整性,我保留了所有代码。在上面的逻辑上可以看到是先是通过getSecureSetting方法获取了Setting对象,然后调用packageValueForCallResult方法传入Setting对象,我们先看getSecureSetting的方法实现

    
    private Setting getSecureSetting(String name, int requestingUserId, boolean enableOverride) {
    
        if (DEBUG) {
    
            Slog.v(LOG_TAG, "getSecureSetting(" + name + ", " + requestingUserId + ")");
    
        }
    
        // Resolve the userId on whose behalf the call is made.
    
        final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);
    
        // Ensure the caller can access the setting.
    
        enforceSettingReadable(name, SETTINGS_TYPE_SECURE, UserHandle.getCallingUserId());
    
        // Determine the owning user as some profile settings are cloned from the parent.
    
        final int owningUserId = resolveOwningUserIdForSecureSettingLocked(callingUserId, name);
    
        if (!isSecureSettingAccessible(name, callingUserId, owningUserId)) {
    
            // This caller is not permitted to access this setting. Pretend the setting doesn't
    
            // exist.
    
            SettingsState settings = mSettingsRegistry.getSettingsLocked(SETTINGS_TYPE_SECURE,
    
                    owningUserId);
    
            return settings != null ? settings.getNullSetting() : null;
    
        }
    
        // As of Android O, the SSAID is read from an app-specific entry in table
    
        // SETTINGS_FILE_SSAID, unless accessed by a system process.
    
        if (isNewSsaidSetting(name)) {
    
            PackageInfo callingPkg = getCallingPackageInfo(owningUserId);
    
            synchronized (mLock) {
    
                return getSsaidSettingLocked(callingPkg, owningUserId);
    
            }
    
        }
    
        if (enableOverride) {
    
            if (Secure.LOCATION_MODE.equals(name)) {
    
                final Setting overridden = getLocationModeSetting(owningUserId);
    
                if (overridden != null) {
    
                    return overridden;
    
                }
    
            }
    
        }
    
        // Not the SSAID; do a straight lookup
    
        synchronized (mLock) {
    
            return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SECURE,
    
                    owningUserId, name);
    
        }
    
    }
    
    private PackageInfo getCallingPackageInfo(int userId) {
    
        try {
    
            return mPackageManager.getPackageInfo(getCallingPackage(),
    
                    PackageManager.GET_SIGNATURES, userId);
    
        } catch (RemoteException e) {
    
            throw new IllegalStateException("Package " + getCallingPackage() + " doesn't exist");
    
        }
    
    }
    
    

    我们的查询Setting的这个操作,会通过 isNewSsaidSetting(name) 这个判断来处理,然后他调用getCallingPackageInfo来查询应用签名信息,最后把存有应用签名信息的PackageInfo传入getSsaidSettingLocked方法中。我们继续查看

    
    private Setting getSsaidSettingLocked(PackageInfo callingPkg, int owningUserId) {
    
        // Get uid of caller (key) used to store ssaid value
    
        // 这个name是uid,是应用ID
    
        String name = Integer.toString(
    
                UserHandle.getUid(owningUserId, UserHandle.getAppId(Binder.getCallingUid())));
    
        if (DEBUG) {
    
            Slog.v(LOG_TAG, "getSsaidSettingLocked(" + name + "," + owningUserId + ")");
    
        }
    
        // Retrieve the ssaid from the table if present.
    
        // 先通过mSettings缓存来获取对应的Setting对象,到这里如果Settings不是空的,其实就已经查出来ssaid了
    
        final Setting ssaid = mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SSAID, owningUserId,name);
    
        // If the app is an Instant App use its stored SSAID instead of our own.
    
        // 获取免安装应用的ssaid,咱们正常安装的apk都是返回空值,具体操作在PackageManagerService下的getInstantAppAndroidId方法,感兴趣的同学可以去自己看一下,这里不延伸了
    
        final String instantSsaid;
    
        final long token = Binder.clearCallingIdentity();
    
        try {
    
            instantSsaid = mPackageManager.getInstantAppAndroidId(callingPkg.packageName,
    
                    owningUserId);
    
        } catch (RemoteException e) {
    
            Slog.e(LOG_TAG, "Failed to get Instant App Android ID", e);
    
            return null;
    
        } finally {
    
            Binder.restoreCallingIdentity(token);
    
        }
    
        final SettingsState ssaidSettings = mSettingsRegistry.getSettingsLocked(
    
                SETTINGS_TYPE_SSAID, owningUserId);
    
        if (instantSsaid != null) {
    
            // Use the stored value if it is still valid.
    
            if (ssaid != null && instantSsaid.equals(ssaid.getValue())) {
    
                return mascaradeSsaidSetting(ssaidSettings, ssaid);
    
            }
    
            // The value has changed, update the stored value.
    
            final boolean success = ssaidSettings.insertSettingLocked(name, instantSsaid, null,
    
                    true, callingPkg.packageName);
    
            if (!success) {
    
                throw new IllegalStateException("Failed to update instant app android id");
    
            }
    
            Setting setting = mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SSAID,
    
                    owningUserId, name);
    
            return mascaradeSsaidSetting(ssaidSettings, setting);
    
        }
    
        // Lazy initialize ssaid if not yet present in ssaid table.
    
        // 这里会做一个检测,当我们的App首次安装的时候,缓存的ssaid列表里是没有Setting对象的
    
        if (ssaid == null || ssaid.isNull() || ssaid.getValue() == null) {
    
            Setting setting = mSettingsRegistry.generateSsaidLocked(callingPkg, owningUserId);
    
            return mascaradeSsaidSetting(ssaidSettings, setting);
    
        }
    
        return mascaradeSsaidSetting(ssaidSettings, ssaid);
    
    }
    
    private Setting mascaradeSsaidSetting(SettingsState settingsState, Setting ssaidSetting) {
    
        // SSAID settings are located in a dedicated table for internal bookkeeping
    
        // but for the world they reside in the secure table, so adjust the key here.
    
        // We have a special name when looking it up but want the world to see it as
    
        // "android_id".
    
        if (ssaidSetting != null) {
    
            return settingsState.new Setting(ssaidSetting) {
    
                @Override
    
                public int getKey() {
    
                    final int userId = getUserIdFromKey(super.getKey());
    
                    return makeKey(SETTINGS_TYPE_SECURE, userId);
    
                }
    
                @Override
    
                public String getName() {
    
                    return Settings.Secure.ANDROID_ID;
    
                }
    
            };
    
        }
    
        return null;
    
    }
    
    private Bundle packageValueForCallResult(Setting setting, boolean trackingGeneration) {
    
        if (!trackingGeneration) {
    
            if (setting == null || setting.isNull()) {
    
                return NULL_SETTING_BUNDLE;
    
            }
    
            return Bundle.forPair(Settings.NameValueTable.VALUE, setting.getValue());
    
        }
    
        Bundle result = new Bundle();
    
        result.putString(Settings.NameValueTable.VALUE,
    
                !setting.isNull() ? setting.getValue() : null);
    
        mSettingsRegistry.mGenerationRegistry.addGenerationData(result, setting.getKey());
    
        return result;
    
    }
    
    

    到这里其实就已经知道整个查询过程了。我在梳理一下整个查询过程。

    1.通过Settings.Secure.getString来发起查询请求

    2.Settings内部会通过SettingsProvider的方式去查询,此时已经通过IPC的方式查询了,处理流已经到了SettingsProvider进程内

    3.SettingsProvider会通过uid进行查询对应的Setting也就是ssaid对象,然后在最后会判断是否为空,因为我们应用首次安装的时候,这个ssaid是空值,系统会生成一遍ssaid,然后在返回封装有ssaid的Setting对象

    4.最后调用packageValueForCallResult方法,把Setting对象转化为Bundle对象并用于进程传递

    接下来看一下生成过程 generateSsaidLocked

    
    public Setting generateSsaidLocked(PackageInfo callingPkg, int userId) {
    
        // Read the user's key from the ssaid table.
    
        // 获取当前用户维度的setting对象
    
        Setting userKeySetting = getSettingLocked(SETTINGS_TYPE_SSAID, userId, SSAID_USER_KEY);
    
        if (userKeySetting == null || userKeySetting.isNull()
    
                || userKeySetting.getValue() == null) {
    
            // Lazy initialize and store the user key.
    
            generateUserKeyLocked(userId);
    
            userKeySetting = getSettingLocked(SETTINGS_TYPE_SSAID, userId, SSAID_USER_KEY);
    
            if (userKeySetting == null || userKeySetting.isNull()
    
                    || userKeySetting.getValue() == null) {
    
                throw new IllegalStateException("User key not accessible");
    
            }
    
        }
    
        final String userKey = userKeySetting.getValue();
    
        if (userKey == null || userKey.length() % 2 != 0) {
    
            throw new IllegalStateException("User key invalid");
    
        }
    
        // Convert the user's key back to a byte array.
    
        final byte[] keyBytes = HexEncoding.decode(userKey);
    
        // Validate that the key is of expected length.
    
        // Keys are currently 32 bytes, but were once 16 bytes during Android O development.
    
        if (keyBytes.length != 16 && keyBytes.length != 32) {
    
            throw new IllegalStateException("User key invalid");
    
        }
    
        //用 userKey 初始化 HmacSHA256 算法
    
        final Mac m;
    
        try {
    
            m = Mac.getInstance("HmacSHA256");
    
            m.init(new SecretKeySpec(keyBytes, m.getAlgorithm()));
    
        } catch (NoSuchAlgorithmException e) {
    
            throw new IllegalStateException("HmacSHA256 is not available", e);
    
        } catch (InvalidKeyException e) {
    
            throw new IllegalStateException("Key is corrupted", e);
    
        }
    
        // Mac each of the developer signatures.
    
        // 通过 callingPkg 对象获取调用应用的签名信息,然后进行 HmacSHA256 加密
    
        for (int i = 0; i < callingPkg.signatures.length; i++) {
    
            byte[] sig = callingPkg.signatures[i].toByteArray();
    
            m.update(getLengthPrefix(sig), 0, 4);
    
            m.update(sig);
    
        }
    
        // Convert result to a string for storage in settings table. Only want first 64 bits.
    
        // 截取前16位字符作为当前应用的AndroidId
    
        final String ssaid = HexEncoding.encodeToString(m.doFinal(), false /* upperCase */)
    
                .substring(0, 16);
    
        // Save the ssaid in the ssaid table.
    
        final String uid = Integer.toString(callingPkg.applicationInfo.uid);
    
        final SettingsState ssaidSettings = getSettingsLocked(SETTINGS_TYPE_SSAID, userId);
    
        //插入到缓存中
    
        final boolean success = ssaidSettings.insertSettingLocked(uid, ssaid, null, true,
    
            callingPkg.packageName);
    
        if (!success) {
    
            throw new IllegalStateException("Ssaid settings not accessible");
    
        }
    
        return getSettingLocked(SETTINGS_TYPE_SSAID, userId, uid);
    
    }
    
    

    其中生成过程也很清楚了,是基于当前用户的随机串(会存储到本地文件,只有初次生成是随机串),加上当前应用的签名信息,采用HmacSHA256的加密方式得到的加密串截取前16位生成出来,所以只要应用签名不会变,手机登录的用户不会变的情况下,ANDROID_ID就不会变

    有些同学可能还不太明白userKey是什么东西。我简单描述下

    userKey 是在设备的系统首次启动的时候,根据当前的userId生成的一个随机串,然后存储到 /data/system/users/0/settings_global.xml 目录下,这个只有在恢复出厂设置、变更用户、刷机的时候才会发生变化

    所以当下8.0以上如果想要区分设备ID,SSAID是当下最好的选择,毕竟Google大法好

    一个被程序耽误的画手
  • 相关阅读:
    表数据转换为insert语句
    Google Map Api 谷歌地图接口整理
    VS预生成事件命令行 和 生成后事件命令行
    C#程序开机运行
    枚举数据源化
    winform分页管理
    数据库访问驱动
    sql时间格式
    sysobjects.xtype介绍
    编码标准的多样性
  • 原文地址:https://www.cnblogs.com/kezhuang/p/14623370.html
Copyright © 2020-2023  润新知