-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
v1.1.4 の ExposureSummary.HighestRiskScore の部分の差分解説 #31
Comments
現象としては、
なので、「接触可能性アリ」の通知は出るが、アプリでは「陽性者との接触確認する」と 0 という現象と思われる。 ExposureSummary.HighestRiskScore >= config.MinimumRiskScore のチェックを入れると
ってことになるけど、iOS 側で EN の履歴を見れば「1件あります」という現象は続くと思われる。 EN API
|
v1.1.4 の修正解析以下は、Exposure Notification - Apple Developer の Source Code から、EN api 関係のコードをダウンロードしてチェック。 コードを引用して解説します。 ポイントin ENExposureDetectionDaemonSession.m
これにより、TEKとの接触はあったが、すべてのリスク値が最小リスク値を下回る場合に、以下の現象となる。
これを v1.1.4 では以下のように修正する。
これにより、通知で接触可能性アリと出るが、アプリでは陽性者数がゼロ件である、という現象はなくなる。 原因detectExposures が返す summary.matchedKeyCount がリスク計算を考慮したもの、と勘違いが原因である。 v1.1.3 以前の現象
v1.1.4 以降の現象
おそらく「接触チェックの記録」のカウントは、addFile の _matchedKeyCount のままのため、最小リスク値は考慮されずカウントは「1」になると考えられる。
という現象が発生すると思う。 ENExposureDetectionDaemonSession.m より抜粋/*
* Find matches for an the TEKs contained with the provided ENFile.
* Returnes NO if the matching process encounters an error, else returns YES.
*/
- (BOOL)addFile:(ENFile *)mainFile
{
__block NSError *error = nil;
uint64_t fileMatchCount = 0;
for( ;; )
{
@autoreleasepool
{
NSMutableArray<ENTemporaryExposureKey *> *tekArray = [[NSMutableArray <ENTemporaryExposureKey *> alloc] init];
check_compile_time_code( TEKBatchSize > 0 );
uint32_t tekCount = 0;
// Match the TEKs in batches to reduce the peak memory usage
for( ; tekCount < TEKBatchSize; ++tekCount )
{
ENTemporaryExposureKey *key = [mainFile readTEKAndReturnError:&error];
if( !key ) break;
[tekArray addObject:key];
}
if( tekCount == 0 ) break;
// マッチした数を返す。リスク値は考慮されない
fileMatchCount += [_databaseQuerySession matchCountForKeys:tekArray attenuationThreshold:0xFF error:&error];
if( error ) break;
}
}
// 単純に、TEK がマッチした値を _matchedKeyCount に保存しておく
_matchedKeyCount += fileMatchCount;
if( error )
{
return NO;
}
return YES;
} - (ENExposureDetectionSummary *)generateSummary
{
// Process all the cached info to create the summary.
__block uint32_t attenuationDurationSums[ 3 ] = { 0, 0, 0 };
__block uint32_t *attenuationDurationSumsPtr = attenuationDurationSums;
__block uint64_t minimumRiskScoreSkipped = 0;
ENRiskScore minimumRiskScore = _configuration.minimumRiskScore;
double minimumRiskScoreFullRange = _configuration.minimumRiskScoreFullRange;
__block ENRiskScore maximumRiskScore = 0;
__block double maximumRiskScoreFullRange = 0;
__block CFAbsoluteTime mostRecentExposureTime = 0;
__block double riskScoreSumFullRange = 0;
CFAbsoluteTime nowTime = CFAbsoluteTimeGetCurrent();
[_databaseQuerySession enumerateCachedExposureInfo:
^( NSArray <ENExposureInfo *> * _Nullable inExposureInfoBatch, NSError * _Nullable __unused inError )
{
for( ENExposureInfo *exposureInfo in inExposureInfoBatch )
{
// Determine if this exposure is the most recent exposure
NSTimeInterval exposureTime = exposureInfo.date.timeIntervalSinceReferenceDate;
if( exposureTime > mostRecentExposureTime ) mostRecentExposureTime = exposureTime;
// Filter out any exposures with a risk score below the configured minimum
double riskScoreFullRange = [self estimateRiskWithExposureInfo:exposureInfo referenceTime:nowTime
transmissionRiskLevel:nil];
ENRiskScore clampedRiskScore = (ENRiskScore) Clamp( riskScoreFullRange, ENRiskScoreMin, ENRiskScoreMax );
// 最小リスク値以下のものは、戻り値に含めない
if( ( clampedRiskScore < minimumRiskScore ) || ( riskScoreFullRange < minimumRiskScoreFullRange ) )
{
// 最小リスク値を下回る場合は、スキップする
++minimumRiskScoreSkipped;
continue;
}
// Update the maximum seen risk score
if( clampedRiskScore > maximumRiskScore ) maximumRiskScore = clampedRiskScore;
if( riskScoreFullRange > maximumRiskScoreFullRange ) maximumRiskScoreFullRange = riskScoreFullRange;
riskScoreSumFullRange += riskScoreFullRange;
// Accumulate the calculated attenuation durations
NSArray <NSNumber *> *attenuationDurations = exposureInfo.attenuationDurations;
if( attenuationDurations.count >= countof( attenuationDurationSums ) )
{
for( size_t i = 0; i < countof( attenuationDurationSums ); ++i )
{
uint32_t durationSum = attenuationDurationSumsPtr[ i ];
if( durationSum >= ENDurationMaxSeconds ) continue;
durationSum += attenuationDurations[ i ].unsignedIntValue;
if( durationSum > ENDurationMaxSeconds ) durationSum = ENDurationMaxSeconds;
attenuationDurationSumsPtr[ i ] = durationSum;
}
}
}
}];
// Round the attenuation durations
NSInteger daysSinceLastExposure = ( mostRecentExposureTime > 0 )
? ( (NSInteger)( ( nowTime - mostRecentExposureTime ) / kSecondsPerDay ) )
: 0;
attenuationDurationSums[ 0 ] = RoundUp( attenuationDurationSums[ 0 ], ENDurationIncrement );
attenuationDurationSums[ 1 ] = RoundUp( attenuationDurationSums[ 1 ], ENDurationIncrement );
attenuationDurationSums[ 2 ] = RoundUp( attenuationDurationSums[ 2 ], ENDurationIncrement );
// Generate the summary
ENExposureDetectionSummary *summary = [[ENExposureDetectionSummary alloc] init];
summary.attenuationDurations =
@[
@(attenuationDurationSums[ 0 ]),
@(attenuationDurationSums[ 1 ]),
@(attenuationDurationSums[ 2 ])
];
summary.daysSinceLastExposure = daysSinceLastExposure;
// マッチカウントは、addFile で保持している _matchedKeyCount を返す
// ゆえに全てのTEKで最小リスク値を
summary.matchedKeyCount = _matchedKeyCount;
summary.maximumRiskScore = maximumRiskScore;
summary.maximumRiskScoreFullRange = maximumRiskScoreFullRange;
summary.riskScoreSumFullRange = riskScoreSumFullRange;
return summary;
} - (NSArray<ENExposureInfo *> *)exposureInfo
{
// Get the ENExposureInfo objects from the database.
__block NSInteger databaseTotal = 0;
__block ENRiskScore minimumRiskScore = _configuration.minimumRiskScore;
double minimumRiskScoreFullRange = _configuration.minimumRiskScoreFullRange;
__block uint64_t minimumRiskScoreSkipped = 0;
CFAbsoluteTime nowTime = CFAbsoluteTimeGetCurrent();
NSMutableArray <ENExposureInfo *> *exposureInfoArray = [[NSMutableArray <ENExposureInfo *> alloc] init];
[_databaseQuerySession enumerateCachedExposureInfo:
^( NSArray <ENExposureInfo *> * _Nullable inExposureInfoBatch, NSError * _Nullable __unused inError )
{
databaseTotal += inExposureInfoBatch.count;
for( ENExposureInfo *exposureInfo in inExposureInfoBatch )
{
// Filter out any exposures with a risk score below the configured minimum
ENRiskLevel transmissionRiskLevel = 0;
double riskScoreFullRange = [self estimateRiskWithExposureInfo:exposureInfo referenceTime:nowTime
transmissionRiskLevel:&transmissionRiskLevel];
ENRiskScore clampedRiskScore = (ENRiskScore) Clamp( riskScoreFullRange, ENRiskScoreMin, ENRiskScoreMax );
// ここの条件式は、generateSummary と全く同じ
if( ( clampedRiskScore < minimumRiskScore ) || ( riskScoreFullRange < minimumRiskScoreFullRange ) )
{
// 最小リスク値であればスキップする
++minimumRiskScoreSkipped;
continue;
}
// Round the attenuation durations
uint32_t duration = (uint32_t) exposureInfo.duration;
duration = RoundUp( duration, ENDurationIncrement );
exposureInfo.duration = Min( duration, ENDurationMaxSeconds );
NSMutableArray <NSNumber *> *filteredAttenuationDurations = [[NSMutableArray <NSNumber *> alloc] init];
for( NSNumber *attenuationDuration in exposureInfo.attenuationDurations )
{
duration = attenuationDuration.unsignedIntValue;
duration = RoundUp( duration, ENDurationIncrement );
duration = Min( duration, ENDurationMaxSeconds );
[filteredAttenuationDurations addObject:@(duration)];
}
// Populate the final ENExposureInfo
exposureInfo.attenuationDurations = filteredAttenuationDurations;
exposureInfo.totalRiskScore = clampedRiskScore;
exposureInfo.totalRiskScoreFullRange = riskScoreFullRange;
exposureInfo.transmissionRiskLevel = transmissionRiskLevel;
// exposureInfoArray に保存しておく
[exposureInfoArray addObject:exposureInfo];
}
}];
// exposureInfoArray を返す
return exposureInfoArray;
} BuildingAnAppToNotifyUsersOfCOVID19Exposure の readme よりServer.shared.getExposureConfiguration { result in
switch result {
case let .success(configuration):
ExposureManager.shared.manager.detectExposures(configuration: configuration, diagnosisKeyURLs: localURLs) { summary, error in
if let error = error {
finish(.failure(error))
return
}
// ここで Summary.HighestRiskScore をチェックせずに、
// getExposureInfo を使うのが、ミスリード?
let userExplanation = NSLocalizedString("USER_NOTIFICATION_EXPLANATION", comment: "User notification")
ExposureManager.shared.manager.getExposureInfo(summary: summary!, userExplanation: userExplanation) { exposures, error in
if let error = error {
finish(.failure(error))
return
}
let newExposures = exposures!.map { exposure in
Exposure(date: exposure.date,
duration: exposure.duration,
totalRiskScore: exposure.totalRiskScore,
transmissionRiskLevel: exposure.transmissionRiskLevel)
}
finish(.success((newExposures, nextDiagnosisKeyFileIndex + localURLs.count)))
}
}
case let .failure(error):
finish(.failure(error))
}
} |
の予定であるが、v1.1.4 でも出るらしい。ぐぬぬ。 |
ちょっと謎な
if (userData.ExposureSummary.HighestRiskScore >= config.MinimumRiskScore)
なところを推測するための記録として。The text was updated successfully, but these errors were encountered: