Export to AOSP TextClassifier. am: 1cc5c21b6e
Original change: https://googleplex-android-review.googlesource.com/c/platform/external/libtextclassifier/+/16264920
Change-Id: I421ede974fd7383426f436a8d3343f4df036a9e7
diff --git a/java/src/com/android/textclassifier/ModelFileManagerImpl.java b/java/src/com/android/textclassifier/ModelFileManagerImpl.java
index 68f77f0..45426d0 100644
--- a/java/src/com/android/textclassifier/ModelFileManagerImpl.java
+++ b/java/src/com/android/textclassifier/ModelFileManagerImpl.java
@@ -378,13 +378,17 @@
@Nullable LocaleList localePreferences,
@Nullable LocaleList detectedLocales) {
Locale targetLocale = findBestModelLocale(localePreferences, detectedLocales);
- if (!isEmptyLocaleList(localePreferences) && !targetLocale.equals(localePreferences.get(0))) {
+ // detectedLocales usually only contains 2-char language (e.g. en), while locale in
+ // localePreferences is usually complete (e.g. en_US). Log only if targetLocale is not a prefix.
+ if (!isEmptyLocaleList(localePreferences)
+ && !localePreferences.get(0).toString().startsWith(targetLocale.toString())) {
TcLog.d(
TAG,
- "locale preference and target locale mismatch. \n Target locale: "
- + targetLocale
- + " \n Preference locale: "
- + localePreferences.get(0));
+ String.format(
+ Locale.US,
+ "localePreference and targetLocale mismatch: preference: %s, target: %s",
+ localePreferences.get(0),
+ targetLocale));
}
return findBestModelFile(modelType, targetLocale);
}
diff --git a/java/src/com/android/textclassifier/downloader/ModelDownloadException.java b/java/src/com/android/textclassifier/downloader/ModelDownloadException.java
index cd7f09b..99d91b8 100644
--- a/java/src/com/android/textclassifier/downloader/ModelDownloadException.java
+++ b/java/src/com/android/textclassifier/downloader/ModelDownloadException.java
@@ -25,48 +25,61 @@
/** Exception thrown when downloading a model. */
final class ModelDownloadException extends RuntimeException {
- // Consistent with TextClassifierDownloadReported.failure_reason
+ // Consistent with TextClassifierDownloadReported.failure_reason. [1, 8, 9] reserved
public static final int UNKNOWN_FAILURE_REASON = 0;
- public static final int FAILED_TO_SCHEDULE = 1;
public static final int FAILED_TO_DOWNLOAD_SERVICE_CONN_BROKEN = 2;
public static final int FAILED_TO_DOWNLOAD_404_ERROR = 3;
public static final int FAILED_TO_DOWNLOAD_OTHER = 4;
public static final int DOWNLOADED_FILE_MISSING = 5;
public static final int FAILED_TO_PARSE_MANIFEST = 6;
public static final int FAILED_TO_VALIDATE_MODEL = 7;
- public static final int FAILED_TO_MOVE_MODEL = 8;
- public static final int WORKER_STOPPED = 9;
/** Error code for a failed download task. */
@Retention(SOURCE)
@IntDef({
UNKNOWN_FAILURE_REASON,
- FAILED_TO_SCHEDULE,
FAILED_TO_DOWNLOAD_SERVICE_CONN_BROKEN,
FAILED_TO_DOWNLOAD_404_ERROR,
FAILED_TO_DOWNLOAD_OTHER,
DOWNLOADED_FILE_MISSING,
FAILED_TO_PARSE_MANIFEST,
- FAILED_TO_VALIDATE_MODEL,
- FAILED_TO_MOVE_MODEL,
- WORKER_STOPPED
+ FAILED_TO_VALIDATE_MODEL
})
public @interface ErrorCode {}
+ public static final int DEFAULT_DOWNLOADER_LIB_ERROR_CODE = -1;
+
private final int errorCode;
+ private final int downloaderLibErrorCode;
+
public ModelDownloadException(@ErrorCode int errorCode, Throwable cause) {
super(cause);
this.errorCode = errorCode;
+ this.downloaderLibErrorCode = DEFAULT_DOWNLOADER_LIB_ERROR_CODE;
}
public ModelDownloadException(@ErrorCode int errorCode, String message) {
super(message);
this.errorCode = errorCode;
+ this.downloaderLibErrorCode = DEFAULT_DOWNLOADER_LIB_ERROR_CODE;
}
+ public ModelDownloadException(
+ @ErrorCode int errorCode, int downloaderLibErrorCode, String message) {
+ super(message);
+ this.errorCode = errorCode;
+ this.downloaderLibErrorCode = downloaderLibErrorCode;
+ }
+
+ /** Returns the error code from Model Downloader itself. */
@ErrorCode
public int getErrorCode() {
return errorCode;
}
+
+ /** Returns the error code from internal HTTP stack. */
+ public int getDownloaderLibErrorCode() {
+ return downloaderLibErrorCode;
+ }
}
diff --git a/java/src/com/android/textclassifier/downloader/ModelDownloadManager.java b/java/src/com/android/textclassifier/downloader/ModelDownloadManager.java
index 3cfc349..b125f13 100644
--- a/java/src/com/android/textclassifier/downloader/ModelDownloadManager.java
+++ b/java/src/com/android/textclassifier/downloader/ModelDownloadManager.java
@@ -16,6 +16,9 @@
package com.android.textclassifier.downloader;
+import static com.android.textclassifier.downloader.TextClassifierDownloadLogger.REASON_TO_SCHEDULE_DEVICE_CONFIG_UPDATED;
+import static com.android.textclassifier.downloader.TextClassifierDownloadLogger.REASON_TO_SCHEDULE_LOCALE_SETTINGS_CHANGED;
+import static com.android.textclassifier.downloader.TextClassifierDownloadLogger.REASON_TO_SCHEDULE_TCS_STARTED;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import android.content.BroadcastReceiver;
@@ -27,6 +30,7 @@
import android.text.TextUtils;
import androidx.work.BackoffPolicy;
import androidx.work.Constraints;
+import androidx.work.Data;
import androidx.work.ExistingWorkPolicy;
import androidx.work.ListenableWorker;
import androidx.work.NetworkType;
@@ -40,12 +44,16 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Enums;
import com.google.common.base.Preconditions;
+import com.google.common.hash.Hashing;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import java.io.File;
+import java.time.Instant;
import java.util.List;
+import java.util.Locale;
+import java.util.UUID;
import javax.annotation.Nullable;
/** Manager to listen to config update and download latest models. */
@@ -128,7 +136,7 @@
}
maybeOverrideLocaleListForTesting();
TcLog.v(TAG, "Try to schedule model download work because TextClassifierService started.");
- scheduleDownloadWork();
+ scheduleDownloadWork(REASON_TO_SCHEDULE_TCS_STARTED);
}
// TODO(licha): Make this private. Let the constructor accept a receiver to enable testing.
@@ -139,7 +147,7 @@
return;
}
TcLog.v(TAG, "Try to schedule model download work because of system locale changes.");
- scheduleDownloadWork();
+ scheduleDownloadWork(REASON_TO_SCHEDULE_LOCALE_SETTINGS_CHANGED);
}
// TODO(licha): Make this private. Let the constructor accept a receiver to enable testing.
@@ -151,7 +159,7 @@
}
maybeOverrideLocaleListForTesting();
TcLog.v(TAG, "Try to schedule model download work because of device config changes.");
- scheduleDownloadWork();
+ scheduleDownloadWork(REASON_TO_SCHEDULE_DEVICE_CONFIG_UPDATED);
}
/** Clean up internal states on destroying. */
@@ -182,7 +190,9 @@
* <p>At any time there will only be at most one work running. If a work is already pending or
* running, the newly scheduled work will be appended as a child of that work.
*/
- private void scheduleDownloadWork() {
+ private void scheduleDownloadWork(int reasonToSchedule) {
+ long workId =
+ Hashing.farmHashFingerprint64().hashUnencodedChars(UUID.randomUUID().toString()).asLong();
NetworkType networkType =
Enums.getIfPresent(NetworkType.class, settings.getManifestDownloadRequiredNetworkType())
.or(NetworkType.UNMETERED);
@@ -200,6 +210,13 @@
BackoffPolicy.EXPONENTIAL,
settings.getModelDownloadBackoffDelayInMillis(),
MILLISECONDS)
+ .setInputData(
+ new Data.Builder()
+ .putLong(ModelDownloadWorker.INPUT_DATA_KEY_WORK_ID, workId)
+ .putLong(
+ ModelDownloadWorker.INPUT_DATA_KEY_SCHEDULED_TIMESTAMP,
+ Instant.now().toEpochMilli())
+ .build())
.build();
ListenableFuture<Operation.State.SUCCESS> enqueueResultFuture =
WorkManager.getInstance(appContext)
@@ -212,11 +229,15 @@
@Override
public void onSuccess(Operation.State.SUCCESS unused) {
TcLog.v(TAG, "Download work scheduled.");
+ TextClassifierDownloadLogger.downloadWorkScheduled(
+ workId, reasonToSchedule, /* failedToSchedule= */ false);
}
@Override
public void onFailure(Throwable t) {
TcLog.e(TAG, "Failed to schedule download work: ", t);
+ TextClassifierDownloadLogger.downloadWorkScheduled(
+ workId, reasonToSchedule, /* failedToSchedule= */ true);
}
},
executorService);
@@ -230,8 +251,10 @@
TcLog.d(
TAG,
String.format(
+ Locale.US,
"Override LocaleList from %s to %s",
- LocaleList.getAdjustedDefault().toLanguageTags(), localeList));
+ LocaleList.getAdjustedDefault().toLanguageTags(),
+ localeList));
LocaleList.setDefault(LocaleList.forLanguageTags(localeList));
}
}
diff --git a/java/src/com/android/textclassifier/downloader/ModelDownloadWorker.java b/java/src/com/android/textclassifier/downloader/ModelDownloadWorker.java
index ebee3c2..6e04e16 100644
--- a/java/src/com/android/textclassifier/downloader/ModelDownloadWorker.java
+++ b/java/src/com/android/textclassifier/downloader/ModelDownloadWorker.java
@@ -32,6 +32,7 @@
import com.android.textclassifier.downloader.DownloadedModelDatabase.Manifest;
import com.android.textclassifier.downloader.DownloadedModelDatabase.ManifestEnrollment;
import com.android.textclassifier.downloader.DownloadedModelDatabase.Model;
+import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
@@ -41,6 +42,7 @@
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.errorprone.annotations.concurrent.GuardedBy;
+import java.time.Clock;
import java.util.ArrayList;
import java.util.Locale;
@@ -48,13 +50,24 @@
public final class ModelDownloadWorker extends ListenableWorker {
private static final String TAG = "ModelDownloadWorker";
+ public static final String INPUT_DATA_KEY_WORK_ID = "ModelDownloadWorker_workId";
+ public static final String INPUT_DATA_KEY_SCHEDULED_TIMESTAMP =
+ "ModelDownloadWorker_scheduledTimestamp";
+
private final ListeningExecutorService executorService;
private final ModelDownloader downloader;
private final DownloadedModelManager downloadedModelManager;
private final TextClassifierSettings settings;
+ private final long workId;
+
+ private final Clock clock;
+ private final long workScheduledTimeMillis;
+
private final Object lock = new Object();
+ private long workStartedTimeMillis = 0;
+
@GuardedBy("lock")
private final ArrayMap<String, ListenableFuture<Void>> pendingDownloads;
@@ -68,6 +81,11 @@
this.settings = new TextClassifierSettings();
this.pendingDownloads = new ArrayMap<>();
this.manifestsToDownload = null;
+
+ this.workId = workerParams.getInputData().getLong(INPUT_DATA_KEY_WORK_ID, 0);
+ this.workScheduledTimeMillis =
+ workerParams.getInputData().getLong(INPUT_DATA_KEY_SCHEDULED_TIMESTAMP, 0);
+ this.clock = Clock.systemUTC();
}
@VisibleForTesting
@@ -77,7 +95,10 @@
ListeningExecutorService executorService,
ModelDownloader modelDownloader,
DownloadedModelManager downloadedModelManager,
- TextClassifierSettings settings) {
+ TextClassifierSettings settings,
+ long workId,
+ Clock clock,
+ long workScheduledTimeMillis) {
super(context, workerParams);
this.executorService = executorService;
this.downloader = modelDownloader;
@@ -85,36 +106,54 @@
this.settings = settings;
this.pendingDownloads = new ArrayMap<>();
this.manifestsToDownload = null;
+ this.workId = workId;
+ this.clock = clock;
+ this.workScheduledTimeMillis = workScheduledTimeMillis;
}
@Override
public final ListenableFuture<ListenableWorker.Result> startWork() {
+ workStartedTimeMillis = getCurrentTimeMillis();
// Notice: startWork() is invoked on the main thread
if (!settings.isModelDownloadManagerEnabled()) {
TcLog.e(TAG, "Model Downloader is disabled. Abort the work.");
+ logDownloadWorkCompleted(
+ TextClassifierDownloadLogger.WORK_RESULT_FAILURE_MODEL_DOWNLOADER_DISABLED);
return Futures.immediateFuture(ListenableWorker.Result.failure());
}
TcLog.v(TAG, "Start download work...");
if (getRunAttemptCount() >= settings.getModelDownloadWorkerMaxAttempts()) {
TcLog.d(TAG, "Max attempt reached. Abort download work.");
+ logDownloadWorkCompleted(
+ TextClassifierDownloadLogger.WORK_RESULT_FAILURE_MAX_RUN_ATTEMPT_REACHED);
return Futures.immediateFuture(ListenableWorker.Result.failure());
}
return FluentFuture.from(Futures.submitAsync(this::checkAndDownloadModels, executorService))
.transform(
- allSucceeded -> {
+ downloadResult -> {
Preconditions.checkNotNull(manifestsToDownload);
downloadedModelManager.onDownloadCompleted(manifestsToDownload);
- TcLog.v(TAG, "Download work completed. Succeeded: " + allSucceeded);
- return allSucceeded
- ? ListenableWorker.Result.success()
- : ListenableWorker.Result.retry();
+ TcLog.v(TAG, "Download work completed: " + downloadResult);
+ if (downloadResult.failureCount() == 0) {
+ logDownloadWorkCompleted(
+ downloadResult.successCount() > 0
+ ? TextClassifierDownloadLogger.WORK_RESULT_SUCCESS_MODEL_DOWNLOADED
+ : TextClassifierDownloadLogger.WORK_RESULT_SUCCESS_NO_UPDATE_AVAILABLE);
+ return ListenableWorker.Result.success();
+ } else {
+ logDownloadWorkCompleted(
+ TextClassifierDownloadLogger.WORK_RESULT_RETRY_MODEL_DOWNLOAD_FAILED);
+ return ListenableWorker.Result.retry();
+ }
},
executorService)
.catching(
Throwable.class,
t -> {
TcLog.e(TAG, "Unexpected Exception during downloading: ", t);
+ logDownloadWorkCompleted(
+ TextClassifierDownloadLogger.WORK_RESULT_RETRY_RUNTIME_EXCEPTION);
return ListenableWorker.Result.retry();
},
executorService);
@@ -155,7 +194,7 @@
* <p>Download tasks will be combined and logged after completion. Return true if all tasks
* succeeded
*/
- private ListenableFuture<Boolean> checkAndDownloadModels() {
+ private ListenableFuture<DownloadResult> checkAndDownloadModels() {
ImmutableList<Locale> localesToDownload = getLocalesToDownload();
ArrayList<ListenableFuture<Boolean>> downloadResultFutures = new ArrayList<>();
ImmutableMap.Builder<String, ManifestsToDownloadByType> manifestsToDownloadBuilder =
@@ -170,7 +209,8 @@
if (bestLocaleTagAndManifestUrl == null) {
TcLog.w(
TAG,
- String.format("No suitable manifest for %s, %s", modelType, locale.toLanguageTag()));
+ String.format(
+ Locale.US, "No suitable manifest for %s, %s", modelType, locale.toLanguageTag()));
continue;
}
String bestLocaleTag = bestLocaleTagAndManifestUrl.first;
@@ -179,8 +219,12 @@
TcLog.d(
TAG,
String.format(
+ Locale.US,
"model type: %s, current locale tag: %s, best locale tag: %s, manifest url: %s",
- modelType, locale.toLanguageTag(), bestLocaleTag, manifestUrl));
+ modelType,
+ locale.toLanguageTag(),
+ bestLocaleTag,
+ manifestUrl));
if (!shouldDownloadManifest(modelType, bestLocaleTag, manifestUrl)) {
continue;
}
@@ -196,11 +240,16 @@
.call(
() -> {
TcLog.v(TAG, "All Download Tasks Completed");
- boolean allSucceeded = true;
+ int successCount = 0;
+ int failureCount = 0;
for (ListenableFuture<Boolean> downloadResultFuture : downloadResultFutures) {
- allSucceeded &= Futures.getDone(downloadResultFuture);
+ if (Futures.getDone(downloadResultFuture)) {
+ successCount += 1;
+ } else {
+ failureCount += 1;
+ }
}
- return allSucceeded;
+ return DownloadResult.create(successCount, failureCount);
},
executorService);
}
@@ -216,8 +265,10 @@
TcLog.w(
TAG,
String.format(
+ Locale.US,
"Manifest failed too many times, stop retrying: %s %d",
- manifestUrl, downloadedManifest.getFailureCounts()));
+ manifestUrl,
+ downloadedManifest.getFailureCounts()));
return false;
} else {
return true;
@@ -236,12 +287,17 @@
*/
private ListenableFuture<Boolean> downloadManifestAndRegister(
@ModelTypeDef String modelType, String localeTag, String manifestUrl) {
+ long downloadStartTimestamp = getCurrentTimeMillis();
return FluentFuture.from(downloadManifest(manifestUrl))
.transform(
unused -> {
downloadedModelManager.registerManifestEnrollment(modelType, localeTag, manifestUrl);
TextClassifierDownloadLogger.downloadSucceeded(
- modelType, manifestUrl, getRunAttemptCount());
+ workId,
+ modelType,
+ manifestUrl,
+ getRunAttemptCount(),
+ getCurrentTimeMillis() - downloadStartTimestamp);
TcLog.d(TAG, "Manifest downloaded and registered: " + manifestUrl);
return true;
},
@@ -251,12 +307,21 @@
t -> {
downloadedModelManager.registerManifestDownloadFailure(manifestUrl);
int errorCode = ModelDownloadException.UNKNOWN_FAILURE_REASON;
+ int downloaderLibErrorCode = 0;
if (t instanceof ModelDownloadException) {
- errorCode = ((ModelDownloadException) t).getErrorCode();
+ ModelDownloadException mde = (ModelDownloadException) t;
+ errorCode = mde.getErrorCode();
+ downloaderLibErrorCode = mde.getDownloaderLibErrorCode();
}
TcLog.e(TAG, "Failed to download manfiest: " + manifestUrl, t);
- TextClassifierDownloadLogger.downloadFailedAndRetry(
- modelType, manifestUrl, errorCode, getRunAttemptCount());
+ TextClassifierDownloadLogger.downloadFailed(
+ workId,
+ modelType,
+ manifestUrl,
+ errorCode,
+ getRunAttemptCount(),
+ downloaderLibErrorCode,
+ getCurrentTimeMillis() - downloadStartTimestamp);
return false;
},
executorService);
@@ -328,6 +393,41 @@
*/
@Override
public final void onStopped() {
- TcLog.d(TAG, String.format("Stop download. Attempt:%d", getRunAttemptCount()));
+ TcLog.d(TAG, String.format(Locale.US, "Stop download. Attempt:%d", getRunAttemptCount()));
+ logDownloadWorkCompleted(TextClassifierDownloadLogger.WORK_RESULT_RETRY_STOPPED_BY_OS);
+ }
+
+ private long getCurrentTimeMillis() {
+ return clock.instant().toEpochMilli();
+ }
+
+ private void logDownloadWorkCompleted(int workResult) {
+ if (workStartedTimeMillis < workScheduledTimeMillis) {
+ TcLog.w(
+ TAG,
+ String.format(
+ Locale.US,
+ "Bad workStartedTimeMillis: %d, workScheduledTimeMillis: %d",
+ workStartedTimeMillis,
+ workScheduledTimeMillis));
+ workStartedTimeMillis = workScheduledTimeMillis;
+ }
+ TextClassifierDownloadLogger.downloadWorkCompleted(
+ workId,
+ workResult,
+ getRunAttemptCount(),
+ workStartedTimeMillis - workScheduledTimeMillis,
+ getCurrentTimeMillis() - workStartedTimeMillis);
+ }
+
+ @AutoValue
+ abstract static class DownloadResult {
+ public abstract int successCount();
+
+ public abstract int failureCount();
+
+ public static DownloadResult create(int successCount, int failureCount) {
+ return new AutoValue_ModelDownloadWorker_DownloadResult(successCount, failureCount);
+ }
}
}
diff --git a/java/src/com/android/textclassifier/downloader/ModelDownloaderImpl.java b/java/src/com/android/textclassifier/downloader/ModelDownloaderImpl.java
index 32edfd2..2244e9a 100644
--- a/java/src/com/android/textclassifier/downloader/ModelDownloaderImpl.java
+++ b/java/src/com/android/textclassifier/downloader/ModelDownloaderImpl.java
@@ -210,9 +210,12 @@
}
@Override
- public void onFailure(
- @ModelDownloadException.ErrorCode int errorCode, String errorMsg) {
- completer.setException(new ModelDownloadException(errorCode, errorMsg));
+ public void onFailure(int downloaderLibErrorCode, String errorMsg) {
+ completer.setException(
+ new ModelDownloadException(
+ ModelDownloadException.FAILED_TO_DOWNLOAD_OTHER,
+ downloaderLibErrorCode,
+ errorMsg));
}
});
return "downlaoderService.download";
diff --git a/java/src/com/android/textclassifier/downloader/ModelDownloaderServiceImpl.java b/java/src/com/android/textclassifier/downloader/ModelDownloaderServiceImpl.java
index e9f75c1..439588b 100644
--- a/java/src/com/android/textclassifier/downloader/ModelDownloaderServiceImpl.java
+++ b/java/src/com/android/textclassifier/downloader/ModelDownloaderServiceImpl.java
@@ -16,6 +16,7 @@
package com.android.textclassifier.downloader;
+import static com.android.textclassifier.downloader.ModelDownloadException.DEFAULT_DOWNLOADER_LIB_ERROR_CODE;
import static com.google.common.base.Predicates.instanceOf;
import static com.google.common.base.Throwables.getCausalChain;
@@ -123,19 +124,17 @@
getCausalChain(t),
instanceOf(RequestException.class),
/* defaultValue= */ null);
- // TODO(licha): might be better to pass back the raw error code (instead of
- // using the ErrorCode defined inside the ModelDownloadException).
- int errorCode =
- (requestException != null
- && requestException.getErrorDetails().getHttpStatusCode() == 404)
- ? ModelDownloadException.FAILED_TO_DOWNLOAD_404_ERROR
- : ModelDownloadException.FAILED_TO_DOWNLOAD_OTHER;
- dispatchOnFailureSafely(callback, errorCode, t);
+ // TODO(b/181805039): Use error code once downloader lib supports it.
+ int downloaderLibErrorCode =
+ requestException != null
+ ? requestException.getErrorDetails().getHttpStatusCode()
+ : DEFAULT_DOWNLOADER_LIB_ERROR_CODE;
+ dispatchOnFailureSafely(callback, downloaderLibErrorCode, t);
}
},
bgExecutorService);
} catch (Throwable t) {
- dispatchOnFailureSafely(callback, ModelDownloadException.FAILED_TO_DOWNLOAD_OTHER, t);
+ dispatchOnFailureSafely(callback, DEFAULT_DOWNLOADER_LIB_ERROR_CODE, t);
}
}
@@ -154,11 +153,9 @@
}
private static void dispatchOnFailureSafely(
- IModelDownloaderCallback callback,
- @ModelDownloadException.ErrorCode int errorCode,
- Throwable throwable) {
+ IModelDownloaderCallback callback, int downloaderLibErrorCode, Throwable throwable) {
try {
- callback.onFailure(errorCode, throwable.getMessage());
+ callback.onFailure(downloaderLibErrorCode, throwable.getMessage());
} catch (RemoteException e) {
TcLog.e(TAG, "Unable to notify failures in download", e);
}
diff --git a/java/src/com/android/textclassifier/downloader/TextClassifierDownloadLogger.java b/java/src/com/android/textclassifier/downloader/TextClassifierDownloadLogger.java
index 048ba6a..7416b00 100644
--- a/java/src/com/android/textclassifier/downloader/TextClassifierDownloadLogger.java
+++ b/java/src/com/android/textclassifier/downloader/TextClassifierDownloadLogger.java
@@ -16,6 +16,8 @@
package com.android.textclassifier.downloader;
+import static com.android.textclassifier.downloader.ModelDownloadException.DEFAULT_DOWNLOADER_LIB_ERROR_CODE;
+
import android.text.TextUtils;
import com.android.textclassifier.common.ModelType;
import com.android.textclassifier.common.ModelType.ModelTypeDef;
@@ -24,20 +26,17 @@
import com.android.textclassifier.downloader.ModelDownloadException.ErrorCode;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
+import java.util.Locale;
/** Logs TextClassifier download event. */
final class TextClassifierDownloadLogger {
private static final String TAG = "TextClassifierDownloadLogger";
// Values for TextClassifierDownloadReported.download_status
- private static final int DOWNLOAD_STATUS_SCHEDULED =
- TextClassifierStatsLog.TEXT_CLASSIFIER_DOWNLOAD_REPORTED__DOWNLOAD_STATUS__SCHEDULED;
private static final int DOWNLOAD_STATUS_SUCCEEDED =
TextClassifierStatsLog.TEXT_CLASSIFIER_DOWNLOAD_REPORTED__DOWNLOAD_STATUS__SUCCEEDED;
private static final int DOWNLOAD_STATUS_FAILED_AND_RETRY =
TextClassifierStatsLog.TEXT_CLASSIFIER_DOWNLOAD_REPORTED__DOWNLOAD_STATUS__FAILED_AND_RETRY;
- private static final int DOWNLOAD_STATUS_FAILED_AND_ABORT =
- TextClassifierStatsLog.TEXT_CLASSIFIER_DOWNLOAD_REPORTED__DOWNLOAD_STATUS__FAILED_AND_ABORT;
private static final int DEFAULT_MODEL_TYPE =
TextClassifierStatsLog.TEXT_CLASSIFIER_DOWNLOAD_REPORTED__MODEL_TYPE__UNKNOWN_MODEL_TYPE;
@@ -89,9 +88,50 @@
.TEXT_CLASSIFIER_DOWNLOAD_REPORTED__FAILURE_REASON__FAILED_TO_VALIDATE_MODEL)
.build();
+ // Reasons to schedule
+ public static final int REASON_TO_SCHEDULE_TCS_STARTED =
+ TextClassifierStatsLog
+ .TEXT_CLASSIFIER_DOWNLOAD_WORK_SCHEDULED__REASON_TO_SCHEDULE__TCS_STARTED;
+ public static final int REASON_TO_SCHEDULE_LOCALE_SETTINGS_CHANGED =
+ TextClassifierStatsLog
+ .TEXT_CLASSIFIER_DOWNLOAD_WORK_SCHEDULED__REASON_TO_SCHEDULE__LOCALE_SETTINGS_CHANGED;
+ public static final int REASON_TO_SCHEDULE_DEVICE_CONFIG_UPDATED =
+ TextClassifierStatsLog
+ .TEXT_CLASSIFIER_DOWNLOAD_WORK_SCHEDULED__REASON_TO_SCHEDULE__DEVICE_CONFIG_UPDATED;
+
+ // Work results
+ public static final int WORK_RESULT_UNKNOWN_WORK_RESULT =
+ TextClassifierStatsLog
+ .TEXT_CLASSIFIER_DOWNLOAD_WORK_COMPLETED__WORK_RESULT__UNKNOWN_WORK_RESULT;
+ public static final int WORK_RESULT_SUCCESS_MODEL_DOWNLOADED =
+ TextClassifierStatsLog
+ .TEXT_CLASSIFIER_DOWNLOAD_WORK_COMPLETED__WORK_RESULT__SUCCESS_MODEL_DOWNLOADED;
+ public static final int WORK_RESULT_SUCCESS_NO_UPDATE_AVAILABLE =
+ TextClassifierStatsLog
+ .TEXT_CLASSIFIER_DOWNLOAD_WORK_COMPLETED__WORK_RESULT__SUCCESS_NO_UPDATE_AVAILABLE;
+ public static final int WORK_RESULT_FAILURE_MODEL_DOWNLOADER_DISABLED =
+ TextClassifierStatsLog
+ .TEXT_CLASSIFIER_DOWNLOAD_WORK_COMPLETED__WORK_RESULT__FAILURE_MODEL_DOWNLOADER_DISABLED;
+ public static final int WORK_RESULT_FAILURE_MAX_RUN_ATTEMPT_REACHED =
+ TextClassifierStatsLog
+ .TEXT_CLASSIFIER_DOWNLOAD_WORK_COMPLETED__WORK_RESULT__FAILURE_MAX_RUN_ATTEMPT_REACHED;
+ public static final int WORK_RESULT_RETRY_MODEL_DOWNLOAD_FAILED =
+ TextClassifierStatsLog
+ .TEXT_CLASSIFIER_DOWNLOAD_WORK_COMPLETED__WORK_RESULT__RETRY_MODEL_DOWNLOAD_FAILED;
+ public static final int WORK_RESULT_RETRY_RUNTIME_EXCEPTION =
+ TextClassifierStatsLog
+ .TEXT_CLASSIFIER_DOWNLOAD_WORK_COMPLETED__WORK_RESULT__RETRY_RUNTIME_EXCEPTION;
+ public static final int WORK_RESULT_RETRY_STOPPED_BY_OS =
+ TextClassifierStatsLog
+ .TEXT_CLASSIFIER_DOWNLOAD_WORK_COMPLETED__WORK_RESULT__RETRY_STOPPED_BY_OS;
+
/** Logs a succeeded download task. */
public static void downloadSucceeded(
- @ModelTypeDef String modelType, String url, int runAttemptCount) {
+ long workId,
+ @ModelTypeDef String modelType,
+ String url,
+ int runAttemptCount,
+ long downloadDurationMillis) {
Preconditions.checkArgument(!TextUtils.isEmpty(url), "url cannot be null/empty");
TextClassifierStatsLog.write(
TextClassifierStatsLog.TEXT_CLASSIFIER_DOWNLOAD_REPORTED,
@@ -101,27 +141,38 @@
url,
DEFAULT_FAILURE_REASON,
runAttemptCount,
- /* downloaderLibErrorCode */ 0,
- /* downloadDurationMillis */ 0,
- /* workId */ 0L);
+ DEFAULT_DOWNLOADER_LIB_ERROR_CODE,
+ downloadDurationMillis,
+ workId);
if (TcLog.ENABLE_FULL_LOGGING) {
TcLog.v(
TAG,
String.format(
+ Locale.US,
"Download Reported: modelType=%s, fileType=%d, status=%d, url=%s, "
- + "failureReason=%d, runAttemptCount=%d",
+ + "failureReason=%d, runAttemptCount=%d, downloaderLibErrorCode=%d, "
+ + "downloadDurationMillis=%d, workId=%d",
MODEL_TYPE_MAP.getOrDefault(modelType, DEFAULT_MODEL_TYPE),
DEFAULT_FILE_TYPE,
DOWNLOAD_STATUS_SUCCEEDED,
url,
DEFAULT_FAILURE_REASON,
- runAttemptCount));
+ runAttemptCount,
+ DEFAULT_DOWNLOADER_LIB_ERROR_CODE,
+ downloadDurationMillis,
+ workId));
}
}
/** Logs a failed download task which will be retried later. */
- public static void downloadFailedAndRetry(
- @ModelTypeDef String modelType, String url, @ErrorCode int errorCode, int runAttemptCount) {
+ public static void downloadFailed(
+ long workId,
+ @ModelTypeDef String modelType,
+ String url,
+ @ErrorCode int errorCode,
+ int runAttemptCount,
+ int downloaderLibErrorCode,
+ long downloadDurationMillis) {
Preconditions.checkArgument(!TextUtils.isEmpty(url), "url cannot be null/empty");
TextClassifierStatsLog.write(
TextClassifierStatsLog.TEXT_CLASSIFIER_DOWNLOAD_REPORTED,
@@ -131,21 +182,74 @@
url,
FAILURE_REASON_MAP.getOrDefault(errorCode, DEFAULT_FAILURE_REASON),
runAttemptCount,
- /* downloaderLibErrorCode */ 0,
- /* downloadDurationMillis */ 0,
- /* workId */ 0L);
+ downloaderLibErrorCode,
+ downloadDurationMillis,
+ workId);
if (TcLog.ENABLE_FULL_LOGGING) {
TcLog.v(
TAG,
String.format(
+ Locale.US,
"Download Reported: modelType=%s, fileType=%d, status=%d, url=%s, "
- + "failureReason=%d, runAttemptCount=%d",
+ + "failureReason=%d, runAttemptCount=%d, downloaderLibErrorCode=%d, "
+ + "downloadDurationMillis=%d, workId=%d",
MODEL_TYPE_MAP.getOrDefault(modelType, DEFAULT_MODEL_TYPE),
DEFAULT_FILE_TYPE,
DOWNLOAD_STATUS_FAILED_AND_RETRY,
url,
FAILURE_REASON_MAP.getOrDefault(errorCode, DEFAULT_FAILURE_REASON),
- runAttemptCount));
+ runAttemptCount,
+ downloaderLibErrorCode,
+ downloadDurationMillis,
+ workId));
+ }
+ }
+
+ public static void downloadWorkScheduled(
+ long workId, int reasonToSchedule, boolean failedToSchedule) {
+ TextClassifierStatsLog.write(
+ TextClassifierStatsLog.TEXT_CLASSIFIER_DOWNLOAD_WORK_SCHEDULED,
+ workId,
+ reasonToSchedule,
+ failedToSchedule);
+ if (TcLog.ENABLE_FULL_LOGGING) {
+ TcLog.v(
+ TAG,
+ String.format(
+ Locale.US,
+ "Download Work Scheduled: workId=%d, reasonToSchedule=%d, failedToSchedule=%b",
+ workId,
+ reasonToSchedule,
+ failedToSchedule));
+ }
+ }
+
+ public static void downloadWorkCompleted(
+ long workId,
+ int workResult,
+ int runAttemptCount,
+ long workScheduledToStartedDurationMillis,
+ long workStartedToEndedDurationMillis) {
+ TextClassifierStatsLog.write(
+ TextClassifierStatsLog.TEXT_CLASSIFIER_DOWNLOAD_WORK_COMPLETED,
+ workId,
+ workResult,
+ runAttemptCount,
+ workScheduledToStartedDurationMillis,
+ workStartedToEndedDurationMillis);
+ if (TcLog.ENABLE_FULL_LOGGING) {
+ TcLog.v(
+ TAG,
+ String.format(
+ Locale.US,
+ "Download Work Completed: workId=%d, result=%d, runAttemptCount=%d, "
+ + "workScheduledToStartedDurationMillis=%d, "
+ + "workStartedToEndedDurationMillis=%d",
+ workId,
+ workResult,
+ runAttemptCount,
+ workScheduledToStartedDurationMillis,
+ workStartedToEndedDurationMillis));
}
}
diff --git a/java/tests/instrumentation/src/com/android/textclassifier/common/statsd/TextClassifierDownloadLoggerTestRule.java b/java/tests/instrumentation/src/com/android/textclassifier/common/statsd/TextClassifierDownloadLoggerTestRule.java
index 6d29815..9c49cb1 100644
--- a/java/tests/instrumentation/src/com/android/textclassifier/common/statsd/TextClassifierDownloadLoggerTestRule.java
+++ b/java/tests/instrumentation/src/com/android/textclassifier/common/statsd/TextClassifierDownloadLoggerTestRule.java
@@ -21,6 +21,8 @@
import com.android.internal.os.StatsdConfigProto.StatsdConfig;
import com.android.os.AtomsProto.Atom;
import com.android.os.AtomsProto.TextClassifierDownloadReported;
+import com.android.os.AtomsProto.TextClassifierDownloadWorkCompleted;
+import com.android.os.AtomsProto.TextClassifierDownloadWorkScheduled;
import com.google.common.collect.ImmutableList;
import java.util.stream.Collectors;
import org.junit.rules.ExternalResource;
@@ -30,38 +32,96 @@
public final class TextClassifierDownloadLoggerTestRule extends ExternalResource {
private static final String TAG = "DownloadLoggerTestRule";
- /** A statsd config ID, which is arbitrary. */
- private static final long CONFIG_ID = 423779;
+ // Statsd config IDs, which are arbitrary.
+ private static final long CONFIG_ID_DOWNLOAD_REPORTED = 423779;
+ private static final long CONFIG_ID_DOWNLOAD_WORK_SCHEDULED = 42;
+ private static final long CONFIG_ID_DOWNLOAD_WORK_COMPLETED = 2021;
private static final long SHORT_TIMEOUT_MS = 1000;
@Override
public void before() throws Exception {
- StatsdTestUtils.cleanup(CONFIG_ID);
+ StatsdTestUtils.cleanup(CONFIG_ID_DOWNLOAD_REPORTED);
+ StatsdTestUtils.cleanup(CONFIG_ID_DOWNLOAD_WORK_SCHEDULED);
+ StatsdTestUtils.cleanup(CONFIG_ID_DOWNLOAD_WORK_COMPLETED);
- StatsdConfig.Builder builder =
+ StatsdConfig.Builder builder1 =
StatsdConfig.newBuilder()
- .setId(CONFIG_ID)
+ .setId(CONFIG_ID_DOWNLOAD_REPORTED)
.addAllowedLogSource(ApplicationProvider.getApplicationContext().getPackageName());
- StatsdTestUtils.addAtomMatcher(builder, Atom.TEXT_CLASSIFIER_DOWNLOAD_REPORTED_FIELD_NUMBER);
- StatsdTestUtils.pushConfig(builder.build());
+ StatsdTestUtils.addAtomMatcher(builder1, Atom.TEXT_CLASSIFIER_DOWNLOAD_REPORTED_FIELD_NUMBER);
+ StatsdTestUtils.pushConfig(builder1.build());
+
+ StatsdConfig.Builder builder2 =
+ StatsdConfig.newBuilder()
+ .setId(CONFIG_ID_DOWNLOAD_WORK_SCHEDULED)
+ .addAllowedLogSource(ApplicationProvider.getApplicationContext().getPackageName());
+ StatsdTestUtils.addAtomMatcher(
+ builder2, Atom.TEXT_CLASSIFIER_DOWNLOAD_WORK_SCHEDULED_FIELD_NUMBER);
+ StatsdTestUtils.pushConfig(builder2.build());
+
+ StatsdConfig.Builder builder3 =
+ StatsdConfig.newBuilder()
+ .setId(CONFIG_ID_DOWNLOAD_WORK_COMPLETED)
+ .addAllowedLogSource(ApplicationProvider.getApplicationContext().getPackageName());
+ StatsdTestUtils.addAtomMatcher(
+ builder3, Atom.TEXT_CLASSIFIER_DOWNLOAD_WORK_COMPLETED_FIELD_NUMBER);
+ StatsdTestUtils.pushConfig(builder3.build());
}
@Override
public void after() {
try {
- StatsdTestUtils.cleanup(CONFIG_ID);
+ StatsdTestUtils.cleanup(CONFIG_ID_DOWNLOAD_REPORTED);
+ StatsdTestUtils.cleanup(CONFIG_ID_DOWNLOAD_WORK_SCHEDULED);
+ StatsdTestUtils.cleanup(CONFIG_ID_DOWNLOAD_WORK_COMPLETED);
} catch (Exception e) {
Log.e(TAG, "Failed to clean up statsd after tests.");
}
}
- /** Gets a list of download atoms written into statsd, sorted by increasing timestamp. */
- public ImmutableList<TextClassifierDownloadReported> getLoggedAtoms() throws Exception {
- ImmutableList<Atom> loggedAtoms = StatsdTestUtils.getLoggedAtoms(CONFIG_ID, SHORT_TIMEOUT_MS);
+ /**
+ * Gets a list of TextClassifierDownloadReported atoms written into statsd, sorted by increasing
+ * timestamp.
+ */
+ public ImmutableList<TextClassifierDownloadReported> getLoggedDownloadReportedAtoms()
+ throws Exception {
+ ImmutableList<Atom> loggedAtoms =
+ StatsdTestUtils.getLoggedAtoms(CONFIG_ID_DOWNLOAD_REPORTED, SHORT_TIMEOUT_MS);
return ImmutableList.copyOf(
loggedAtoms.stream()
+ .filter(Atom::hasTextClassifierDownloadReported)
.map(Atom::getTextClassifierDownloadReported)
.collect(Collectors.toList()));
}
+
+ /**
+ * Gets a list of TextClassifierDownloadWorkScheduled atoms written into statsd, sorted by
+ * increasing timestamp.
+ */
+ public ImmutableList<TextClassifierDownloadWorkScheduled> getLoggedDownloadWorkScheduledAtoms()
+ throws Exception {
+ ImmutableList<Atom> loggedAtoms =
+ StatsdTestUtils.getLoggedAtoms(CONFIG_ID_DOWNLOAD_WORK_SCHEDULED, SHORT_TIMEOUT_MS);
+ return ImmutableList.copyOf(
+ loggedAtoms.stream()
+ .filter(Atom::hasTextClassifierDownloadWorkScheduled)
+ .map(Atom::getTextClassifierDownloadWorkScheduled)
+ .collect(Collectors.toList()));
+ }
+
+ /**
+ * Gets a list of TextClassifierDownloadWorkCompleted atoms written into statsd, sorted by
+ * increasing timestamp.
+ */
+ public ImmutableList<TextClassifierDownloadWorkCompleted> getLoggedDownloadWorkCompletedAtoms()
+ throws Exception {
+ ImmutableList<Atom> loggedAtoms =
+ StatsdTestUtils.getLoggedAtoms(CONFIG_ID_DOWNLOAD_WORK_COMPLETED, SHORT_TIMEOUT_MS);
+ return ImmutableList.copyOf(
+ loggedAtoms.stream()
+ .filter(Atom::hasTextClassifierDownloadWorkCompleted)
+ .map(Atom::getTextClassifierDownloadWorkCompleted)
+ .collect(Collectors.toList()));
+ }
}
diff --git a/java/tests/instrumentation/src/com/android/textclassifier/downloader/ModelDownloadExceptionTest.java b/java/tests/instrumentation/src/com/android/textclassifier/downloader/ModelDownloadExceptionTest.java
index 794ead9..1e878b2 100644
--- a/java/tests/instrumentation/src/com/android/textclassifier/downloader/ModelDownloadExceptionTest.java
+++ b/java/tests/instrumentation/src/com/android/textclassifier/downloader/ModelDownloadExceptionTest.java
@@ -24,12 +24,30 @@
@RunWith(JUnit4.class)
public final class ModelDownloadExceptionTest {
- private static final int ERROR_CODE = ModelDownloadException.WORKER_STOPPED;
+ private static final int ERROR_CODE = ModelDownloadException.FAILED_TO_DOWNLOAD_OTHER;
+ private static final int DOWNLOADER_LIB_ERROR_CODE = 500;
@Test
- public void getErrorCode() {
- assertThat(new ModelDownloadException(ERROR_CODE, new Exception()).getErrorCode())
- .isEqualTo(ERROR_CODE);
- assertThat(new ModelDownloadException(ERROR_CODE, "").getErrorCode()).isEqualTo(ERROR_CODE);
+ public void getErrorCode_constructor1() {
+ ModelDownloadException e = new ModelDownloadException(ERROR_CODE, new Exception());
+ assertThat(e.getErrorCode()).isEqualTo(ERROR_CODE);
+ assertThat(e.getDownloaderLibErrorCode())
+ .isEqualTo(ModelDownloadException.DEFAULT_DOWNLOADER_LIB_ERROR_CODE);
+ }
+
+ @Test
+ public void getErrorCode_constructor2() {
+ ModelDownloadException e = new ModelDownloadException(ERROR_CODE, "error_msg");
+ assertThat(e.getErrorCode()).isEqualTo(ERROR_CODE);
+ assertThat(e.getDownloaderLibErrorCode())
+ .isEqualTo(ModelDownloadException.DEFAULT_DOWNLOADER_LIB_ERROR_CODE);
+ }
+
+ @Test
+ public void getErrorCode_constructor3() {
+ ModelDownloadException e =
+ new ModelDownloadException(ERROR_CODE, DOWNLOADER_LIB_ERROR_CODE, "error_msg");
+ assertThat(e.getErrorCode()).isEqualTo(ERROR_CODE);
+ assertThat(e.getDownloaderLibErrorCode()).isEqualTo(DOWNLOADER_LIB_ERROR_CODE);
}
}
diff --git a/java/tests/instrumentation/src/com/android/textclassifier/downloader/ModelDownloadManagerTest.java b/java/tests/instrumentation/src/com/android/textclassifier/downloader/ModelDownloadManagerTest.java
index dc86628..c626ed7 100644
--- a/java/tests/instrumentation/src/com/android/textclassifier/downloader/ModelDownloadManagerTest.java
+++ b/java/tests/instrumentation/src/com/android/textclassifier/downloader/ModelDownloadManagerTest.java
@@ -26,6 +26,8 @@
import androidx.work.WorkInfo;
import androidx.work.WorkManager;
import androidx.work.testing.WorkManagerTestInitHelper;
+import com.android.os.AtomsProto.TextClassifierDownloadWorkScheduled;
+import com.android.os.AtomsProto.TextClassifierDownloadWorkScheduled.ReasonToSchedule;
import com.android.textclassifier.common.ModelType;
import com.android.textclassifier.common.TextClassifierSettings;
import com.android.textclassifier.common.statsd.TextClassifierDownloadLoggerTestRule;
@@ -59,7 +61,6 @@
public final TextClassifierDownloadLoggerTestRule loggerTestRule =
new TextClassifierDownloadLoggerTestRule();
- // TODO(licha): Maybe we can just use the real TextClassifierSettings
private TestingDeviceConfig deviceConfig;
private WorkManager workManager;
private ModelDownloadManager downloadManager;
@@ -101,6 +102,7 @@
DownloaderTestUtils.queryWorkInfos(
workManager, ModelDownloadManager.UNIQUE_QUEUE_NAME));
assertThat(workInfo.getState()).isEqualTo(WorkInfo.State.ENQUEUED);
+ verifyWorkScheduledLogging(ReasonToSchedule.TCS_STARTED);
}
@Test
@@ -111,6 +113,7 @@
assertThat(Locale.getDefault()).isEqualTo(Locale.forLanguageTag("zh"));
assertThat(LocaleList.getDefault()).isEqualTo(LocaleList.forLanguageTags("zh,fr"));
assertThat(LocaleList.getAdjustedDefault()).isEqualTo(LocaleList.forLanguageTags("zh,fr"));
+ verifyWorkScheduledLogging(ReasonToSchedule.TCS_STARTED);
}
@Test
@@ -122,6 +125,7 @@
DownloaderTestUtils.queryWorkInfos(
workManager, ModelDownloadManager.UNIQUE_QUEUE_NAME));
assertThat(workInfo.getState()).isEqualTo(WorkInfo.State.ENQUEUED);
+ verifyWorkScheduledLogging(ReasonToSchedule.LOCALE_SETTINGS_CHANGED);
}
@Test
@@ -133,6 +137,7 @@
DownloaderTestUtils.queryWorkInfos(
workManager, ModelDownloadManager.UNIQUE_QUEUE_NAME));
assertThat(workInfo.getState()).isEqualTo(WorkInfo.State.ENQUEUED);
+ verifyWorkScheduledLogging(ReasonToSchedule.DEVICE_CONFIG_UPDATED);
}
@Test
@@ -143,6 +148,7 @@
assertThat(
DownloaderTestUtils.queryWorkInfos(workManager, ModelDownloadManager.UNIQUE_QUEUE_NAME))
.isEmpty();
+ assertThat(loggerTestRule.getLoggedDownloadWorkScheduledAtoms()).isEmpty();
}
@Test
@@ -154,6 +160,11 @@
assertThat(workInfos.stream().map(WorkInfo::getState).collect(Collectors.toList()))
.containsExactly(WorkInfo.State.ENQUEUED, WorkInfo.State.BLOCKED);
+ List<TextClassifierDownloadWorkScheduled> atoms =
+ loggerTestRule.getLoggedDownloadWorkScheduledAtoms();
+ assertThat(atoms).hasSize(2);
+ verifyWorkScheduledAtom(atoms.get(0), ReasonToSchedule.DEVICE_CONFIG_UPDATED);
+ verifyWorkScheduledAtom(atoms.get(1), ReasonToSchedule.DEVICE_CONFIG_UPDATED);
}
@Test
@@ -164,6 +175,7 @@
assertThat(Locale.getDefault()).isEqualTo(Locale.forLanguageTag("zh"));
assertThat(LocaleList.getDefault()).isEqualTo(LocaleList.forLanguageTags("zh,fr"));
assertThat(LocaleList.getAdjustedDefault()).isEqualTo(LocaleList.forLanguageTags("zh,fr"));
+ verifyWorkScheduledLogging(ReasonToSchedule.DEVICE_CONFIG_UPDATED);
}
@Test
@@ -173,4 +185,16 @@
assertThat(downloadManager.listDownloadedModels(MODEL_TYPE)).containsExactly(modelFile);
}
+
+ private void verifyWorkScheduledLogging(ReasonToSchedule reasonToSchedule) throws Exception {
+ TextClassifierDownloadWorkScheduled atom =
+ Iterables.getOnlyElement(loggerTestRule.getLoggedDownloadWorkScheduledAtoms());
+ verifyWorkScheduledAtom(atom, reasonToSchedule);
+ }
+
+ private void verifyWorkScheduledAtom(
+ TextClassifierDownloadWorkScheduled atom, ReasonToSchedule reasonToSchedule) {
+ assertThat(atom.getReasonToSchedule()).isEqualTo(reasonToSchedule);
+ assertThat(atom.getFailedToSchedule()).isFalse();
+ }
}
diff --git a/java/tests/instrumentation/src/com/android/textclassifier/downloader/ModelDownloadWorkerTest.java b/java/tests/instrumentation/src/com/android/textclassifier/downloader/ModelDownloadWorkerTest.java
index 7831697..3646934 100644
--- a/java/tests/instrumentation/src/com/android/textclassifier/downloader/ModelDownloadWorkerTest.java
+++ b/java/tests/instrumentation/src/com/android/textclassifier/downloader/ModelDownloadWorkerTest.java
@@ -32,6 +32,8 @@
import com.android.os.AtomsProto.TextClassifierDownloadReported;
import com.android.os.AtomsProto.TextClassifierDownloadReported.DownloadStatus;
import com.android.os.AtomsProto.TextClassifierDownloadReported.FailureReason;
+import com.android.os.AtomsProto.TextClassifierDownloadWorkCompleted;
+import com.android.os.AtomsProto.TextClassifierDownloadWorkCompleted.WorkResult;
import com.android.textclassifier.common.ModelType;
import com.android.textclassifier.common.TextClassifierSettings;
import com.android.textclassifier.common.statsd.TextClassifierDownloadLoggerTestRule;
@@ -41,6 +43,8 @@
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.MoreExecutors;
import java.io.File;
+import java.time.Clock;
+import java.time.Instant;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
@@ -55,6 +59,7 @@
@RunWith(JUnit4.class)
public final class ModelDownloadWorkerTest {
+ private static final long WORK_ID = 123456789L;
private static final String MODEL_TYPE = ModelType.ANNOTATOR;
private static final String MODEL_TYPE_2 = ModelType.ACTIONS_SUGGESTIONS;
private static final TextClassifierDownloadReported.ModelType MODEL_TYPE_ATOM =
@@ -112,7 +117,24 @@
new LocaleList(new Locale(LOCALE_TAG), new Locale(LOCALE_TAG_2));
private static final LocaleList LOCALE_LIST_3 =
new LocaleList(new Locale(LOCALE_TAG), new Locale(LOCALE_TAG_2), new Locale(LOCALE_TAG_3));
+ private static final Instant WORK_SCHEDULED_TIME = Instant.now();
+ private static final Instant WORK_STARTED_TIME = WORK_SCHEDULED_TIME.plusSeconds(100);
+ // Make sure any combination has a different diff
+ private static final Instant DOWNLOAD_STARTED_TIME = WORK_STARTED_TIME.plusSeconds(1);
+ private static final Instant DOWNLOAD_ENDED_TIME = WORK_STARTED_TIME.plusSeconds(1 + 2);
+ private static final Instant DOWNLOAD_STARTED_TIME_2 = WORK_STARTED_TIME.plusSeconds(1 + 2 + 3);
+ private static final Instant DOWNLOAD_ENDED_TIME_2 = WORK_STARTED_TIME.plusSeconds(1 + 2 + 3 + 4);
+ private static final Instant WORK_ENDED_TIME = WORK_STARTED_TIME.plusSeconds(1 + 2 + 3 + 4 + 5);
+ private static final long DOWNLOAD_STARTED_TO_ENDED_MILLIS =
+ DOWNLOAD_ENDED_TIME.toEpochMilli() - DOWNLOAD_STARTED_TIME.toEpochMilli();
+ private static final long DOWNLOAD_STARTED_TO_ENDED_2_MILLIS =
+ DOWNLOAD_ENDED_TIME_2.toEpochMilli() - DOWNLOAD_STARTED_TIME_2.toEpochMilli();
+ private static final long WORK_SCHEDULED_TO_STARTED_MILLIS =
+ WORK_STARTED_TIME.toEpochMilli() - WORK_SCHEDULED_TIME.toEpochMilli();
+ private static final long WORK_STARTED_TO_ENDED_MILLIS =
+ WORK_ENDED_TIME.toEpochMilli() - WORK_STARTED_TIME.toEpochMilli();
+ @Mock private Clock clock;
@Mock private ModelDownloader modelDownloader;
private File modelDownloaderDir;
private File modelFile;
@@ -163,12 +185,15 @@
modelFile.createNewFile();
when(modelDownloader.downloadModel(modelDownloaderDir, MODEL_PROTO))
.thenReturn(Futures.immediateFuture(modelFile));
+ when(clock.instant())
+ .thenReturn(WORK_STARTED_TIME, DOWNLOAD_STARTED_TIME, DOWNLOAD_ENDED_TIME, WORK_ENDED_TIME);
ModelDownloadWorker worker = createWorker(RUN_ATTEMPT_COUNT);
assertThat(worker.startWork().get()).isEqualTo(ListenableWorker.Result.success());
assertThat(modelFile.exists()).isTrue();
assertThat(downloadedModelManager.listModels(MODEL_TYPE)).containsExactly(modelFile);
- verifyLoggedAtom(DownloadStatus.SUCCEEDED, RUN_ATTEMPT_COUNT, /* failureReason= */ null);
+ verifySucceededDownloadLogging();
+ verifyWorkLogging(RUN_ATTEMPT_COUNT, WorkResult.SUCCESS_MODEL_DOWNLOADED);
}
@Test
@@ -178,12 +203,15 @@
.thenReturn(Futures.immediateFuture(MODEL_MANIFEST_PROTO));
modelFile.createNewFile();
downloadedModelManager.registerModel(MODEL_URL, modelFile.getAbsolutePath());
+ when(clock.instant())
+ .thenReturn(WORK_STARTED_TIME, DOWNLOAD_STARTED_TIME, DOWNLOAD_ENDED_TIME, WORK_ENDED_TIME);
ModelDownloadWorker worker = createWorker(RUN_ATTEMPT_COUNT);
assertThat(worker.startWork().get()).isEqualTo(ListenableWorker.Result.success());
assertThat(modelFile.exists()).isTrue();
assertThat(downloadedModelManager.listModels(MODEL_TYPE)).containsExactly(modelFile);
- verifyLoggedAtom(DownloadStatus.SUCCEEDED, RUN_ATTEMPT_COUNT, /* failureReason= */ null);
+ verifySucceededDownloadLogging();
+ verifyWorkLogging(RUN_ATTEMPT_COUNT, WorkResult.SUCCESS_MODEL_DOWNLOADED);
}
@Test
@@ -192,12 +220,15 @@
modelFile.createNewFile();
downloadedModelManager.registerModel(MODEL_URL, modelFile.getAbsolutePath());
downloadedModelManager.registerManifest(MANIFEST_URL, MODEL_URL);
+ when(clock.instant())
+ .thenReturn(WORK_STARTED_TIME, DOWNLOAD_STARTED_TIME, DOWNLOAD_ENDED_TIME, WORK_ENDED_TIME);
ModelDownloadWorker worker = createWorker(RUN_ATTEMPT_COUNT);
assertThat(worker.startWork().get()).isEqualTo(ListenableWorker.Result.success());
assertThat(modelFile.exists()).isTrue();
assertThat(downloadedModelManager.listModels(MODEL_TYPE)).containsExactly(modelFile);
- verifyLoggedAtom(DownloadStatus.SUCCEEDED, RUN_ATTEMPT_COUNT, /* failureReason= */ null);
+ verifySucceededDownloadLogging();
+ verifyWorkLogging(RUN_ATTEMPT_COUNT, WorkResult.SUCCESS_MODEL_DOWNLOADED);
}
@Test
@@ -214,6 +245,15 @@
.thenReturn(Futures.immediateFuture(modelFile));
when(modelDownloader.downloadModel(modelDownloaderDir, MODEL_PROTO_2))
.thenReturn(Futures.immediateFuture(modelFile2));
+ // We assume we always download MODEL_TYPE first and then MODEL_TYPE_2, o/w this will be flaky
+ when(clock.instant())
+ .thenReturn(
+ WORK_STARTED_TIME,
+ DOWNLOAD_STARTED_TIME,
+ DOWNLOAD_ENDED_TIME,
+ DOWNLOAD_STARTED_TIME_2,
+ DOWNLOAD_ENDED_TIME_2,
+ WORK_ENDED_TIME);
ModelDownloadWorker worker = createWorker(RUN_ATTEMPT_COUNT);
assertThat(worker.startWork().get()).isEqualTo(ListenableWorker.Result.success());
@@ -221,7 +261,7 @@
assertThat(modelFile2.exists()).isTrue();
assertThat(downloadedModelManager.listModels(MODEL_TYPE)).containsExactly(modelFile);
assertThat(downloadedModelManager.listModels(MODEL_TYPE_2)).containsExactly(modelFile2);
- List<TextClassifierDownloadReported> atoms = loggerTestRule.getLoggedAtoms();
+ List<TextClassifierDownloadReported> atoms = loggerTestRule.getLoggedDownloadReportedAtoms();
assertThat(atoms).hasSize(2);
assertThat(
atoms.stream()
@@ -230,6 +270,12 @@
.containsExactly(MANIFEST_URL, MANIFEST_URL_2);
assertThat(atoms.get(0).getDownloadStatus()).isEqualTo(DownloadStatus.SUCCEEDED);
assertThat(atoms.get(1).getDownloadStatus()).isEqualTo(DownloadStatus.SUCCEEDED);
+ assertThat(
+ atoms.stream()
+ .map(TextClassifierDownloadReported::getDownloadDurationMillis)
+ .collect(Collectors.toList()))
+ .containsExactly(DOWNLOAD_STARTED_TO_ENDED_MILLIS, DOWNLOAD_STARTED_TO_ENDED_2_MILLIS);
+ verifyWorkLogging(RUN_ATTEMPT_COUNT, WorkResult.SUCCESS_MODEL_DOWNLOADED);
}
@Test
@@ -243,6 +289,15 @@
modelFile.createNewFile();
when(modelDownloader.downloadModel(modelDownloaderDir, MODEL_PROTO))
.thenReturn(Futures.immediateFuture(modelFile));
+ // We assume we always download MODEL_TYPE first and then MODEL_TYPE_2, o/w this will be flaky
+ when(clock.instant())
+ .thenReturn(
+ WORK_STARTED_TIME,
+ DOWNLOAD_STARTED_TIME,
+ DOWNLOAD_ENDED_TIME,
+ DOWNLOAD_STARTED_TIME_2,
+ DOWNLOAD_ENDED_TIME_2,
+ WORK_ENDED_TIME);
ModelDownloadWorker worker = createWorker(RUN_ATTEMPT_COUNT);
assertThat(worker.startWork().get()).isEqualTo(ListenableWorker.Result.success());
@@ -250,7 +305,7 @@
assertThat(downloadedModelManager.listModels(MODEL_TYPE)).containsExactly(modelFile);
assertThat(downloadedModelManager.listModels(MODEL_TYPE_2)).containsExactly(modelFile);
verify(modelDownloader, times(1)).downloadModel(modelDownloaderDir, MODEL_PROTO);
- List<TextClassifierDownloadReported> atoms = loggerTestRule.getLoggedAtoms();
+ List<TextClassifierDownloadReported> atoms = loggerTestRule.getLoggedDownloadReportedAtoms();
assertThat(atoms).hasSize(2);
assertThat(
atoms.stream()
@@ -259,6 +314,12 @@
.containsExactly(MANIFEST_URL, MANIFEST_URL_2);
assertThat(atoms.get(0).getDownloadStatus()).isEqualTo(DownloadStatus.SUCCEEDED);
assertThat(atoms.get(1).getDownloadStatus()).isEqualTo(DownloadStatus.SUCCEEDED);
+ assertThat(
+ atoms.stream()
+ .map(TextClassifierDownloadReported::getDownloadDurationMillis)
+ .collect(Collectors.toList()))
+ .containsExactly(DOWNLOAD_STARTED_TO_ENDED_MILLIS, DOWNLOAD_STARTED_TO_ENDED_2_MILLIS);
+ verifyWorkLogging(RUN_ATTEMPT_COUNT, WorkResult.SUCCESS_MODEL_DOWNLOADED);
}
@Test
@@ -270,6 +331,15 @@
modelFile.createNewFile();
when(modelDownloader.downloadModel(modelDownloaderDir, MODEL_PROTO))
.thenReturn(Futures.immediateFuture(modelFile));
+ // We assume we always download MODEL_TYPE first and then MODEL_TYPE_2, o/w this will be flaky
+ when(clock.instant())
+ .thenReturn(
+ WORK_STARTED_TIME,
+ DOWNLOAD_STARTED_TIME,
+ DOWNLOAD_ENDED_TIME,
+ DOWNLOAD_STARTED_TIME_2,
+ DOWNLOAD_ENDED_TIME_2,
+ WORK_ENDED_TIME);
ModelDownloadWorker worker = createWorker(RUN_ATTEMPT_COUNT);
assertThat(worker.startWork().get()).isEqualTo(ListenableWorker.Result.success());
@@ -277,7 +347,7 @@
assertThat(downloadedModelManager.listModels(MODEL_TYPE)).containsExactly(modelFile);
assertThat(downloadedModelManager.listModels(MODEL_TYPE_2)).containsExactly(modelFile);
verify(modelDownloader, times(1)).downloadManifest(MANIFEST_URL);
- List<TextClassifierDownloadReported> atoms = loggerTestRule.getLoggedAtoms();
+ List<TextClassifierDownloadReported> atoms = loggerTestRule.getLoggedDownloadReportedAtoms();
assertThat(atoms).hasSize(2);
assertThat(
atoms.stream()
@@ -286,6 +356,12 @@
.containsExactly(MANIFEST_URL, MANIFEST_URL);
assertThat(atoms.get(0).getDownloadStatus()).isEqualTo(DownloadStatus.SUCCEEDED);
assertThat(atoms.get(1).getDownloadStatus()).isEqualTo(DownloadStatus.SUCCEEDED);
+ assertThat(
+ atoms.stream()
+ .map(TextClassifierDownloadReported::getDownloadDurationMillis)
+ .collect(Collectors.toList()))
+ .containsExactly(DOWNLOAD_STARTED_TO_ENDED_MILLIS, DOWNLOAD_STARTED_TO_ENDED_2_MILLIS);
+ verifyWorkLogging(RUN_ATTEMPT_COUNT, WorkResult.SUCCESS_MODEL_DOWNLOADED);
}
@Test
@@ -293,11 +369,13 @@
setUpManifestUrl(MODEL_TYPE, LOCALE_TAG, MANIFEST_URL);
when(modelDownloader.downloadManifest(MANIFEST_URL))
.thenReturn(Futures.immediateFailedFuture(FAILED_TO_DOWNLOAD_EXCEPTION));
+ when(clock.instant())
+ .thenReturn(WORK_STARTED_TIME, DOWNLOAD_STARTED_TIME, DOWNLOAD_ENDED_TIME, WORK_ENDED_TIME);
ModelDownloadWorker worker = createWorker(RUN_ATTEMPT_COUNT);
assertThat(worker.startWork().get()).isEqualTo(ListenableWorker.Result.retry());
- verifyLoggedAtom(
- DownloadStatus.FAILED_AND_RETRY, RUN_ATTEMPT_COUNT, FAILED_TO_DOWNLOAD_FAILURE_REASON);
+ verifyFailedDownloadLogging();
+ verifyWorkLogging(RUN_ATTEMPT_COUNT, WorkResult.RETRY_MODEL_DOWNLOAD_FAILED);
}
@Test
@@ -307,21 +385,39 @@
.thenReturn(Futures.immediateFuture(MODEL_MANIFEST_PROTO));
when(modelDownloader.downloadModel(modelDownloaderDir, MODEL_PROTO))
.thenReturn(Futures.immediateFailedFuture(FAILED_TO_DOWNLOAD_EXCEPTION));
+ when(clock.instant())
+ .thenReturn(WORK_STARTED_TIME, DOWNLOAD_STARTED_TIME, DOWNLOAD_ENDED_TIME, WORK_ENDED_TIME);
ModelDownloadWorker worker = createWorker(RUN_ATTEMPT_COUNT);
assertThat(worker.startWork().get()).isEqualTo(ListenableWorker.Result.retry());
- verifyLoggedAtom(
- DownloadStatus.FAILED_AND_RETRY, RUN_ATTEMPT_COUNT, FAILED_TO_DOWNLOAD_FAILURE_REASON);
+ verifyFailedDownloadLogging();
+ verifyWorkLogging(RUN_ATTEMPT_COUNT, WorkResult.RETRY_MODEL_DOWNLOAD_FAILED);
+ }
+
+ @Test
+ public void downloadFailed_modelDownloadManagerDisabled() throws Exception {
+ deviceConfig.setConfig(TextClassifierSettings.MODEL_DOWNLOAD_MANAGER_ENABLED, false);
+ when(clock.instant()).thenReturn(WORK_STARTED_TIME, WORK_ENDED_TIME);
+
+ ModelDownloadWorker worker = createWorker(RUN_ATTEMPT_COUNT);
+ assertThat(worker.startWork().get()).isEqualTo(ListenableWorker.Result.failure());
+ assertThat(loggerTestRule.getLoggedDownloadReportedAtoms()).isEmpty();
+ verifyWorkLogging(RUN_ATTEMPT_COUNT, WorkResult.FAILURE_MODEL_DOWNLOADER_DISABLED);
}
@Test
public void downloadFailed_reachWorkerMaxRunAttempts() throws Exception {
+ when(clock.instant()).thenReturn(WORK_STARTED_TIME, WORK_ENDED_TIME);
+
ModelDownloadWorker worker = createWorker(WORKER_MAX_RUN_ATTEMPT_COUNT);
assertThat(worker.startWork().get()).isEqualTo(ListenableWorker.Result.failure());
+ assertThat(loggerTestRule.getLoggedDownloadReportedAtoms()).isEmpty();
+ verifyWorkLogging(WORKER_MAX_RUN_ATTEMPT_COUNT, WorkResult.FAILURE_MAX_RUN_ATTEMPT_REACHED);
}
@Test
public void downloadSkipped_reachManifestMaxAttempts() throws Exception {
+ when(clock.instant()).thenReturn(WORK_STARTED_TIME, WORK_ENDED_TIME);
setUpManifestUrl(MODEL_TYPE, LOCALE_TAG, MANIFEST_URL);
deviceConfig.setConfig(
TextClassifierSettings.MANIFEST_DOWNLOAD_MAX_ATTEMPTS, MANIFEST_MAX_ATTEMPT_COUNT);
@@ -332,20 +428,23 @@
ModelDownloadWorker worker = createWorker(MANIFEST_MAX_ATTEMPT_COUNT);
assertThat(worker.startWork().get()).isEqualTo(ListenableWorker.Result.success());
- assertThat(loggerTestRule.getLoggedAtoms()).isEmpty();
+ assertThat(loggerTestRule.getLoggedDownloadReportedAtoms()).isEmpty();
+ verifyWorkLogging(MANIFEST_MAX_ATTEMPT_COUNT, WorkResult.SUCCESS_NO_UPDATE_AVAILABLE);
}
@Test
public void downloadSkipped_manifestAlreadyProcessed() throws Exception {
+ when(clock.instant()).thenReturn(WORK_STARTED_TIME, WORK_ENDED_TIME);
setUpManifestUrl(MODEL_TYPE, LOCALE_TAG, MANIFEST_URL);
modelFile.createNewFile();
downloadedModelManager.registerModel(MODEL_URL, modelFile.getAbsolutePath());
downloadedModelManager.registerManifest(MANIFEST_URL, MODEL_URL);
downloadedModelManager.registerManifestEnrollment(MODEL_TYPE, LOCALE_TAG, MANIFEST_URL);
- ModelDownloadWorker worker = createWorker(MANIFEST_MAX_ATTEMPT_COUNT);
+ ModelDownloadWorker worker = createWorker(RUN_ATTEMPT_COUNT);
assertThat(worker.startWork().get()).isEqualTo(ListenableWorker.Result.success());
- assertThat(loggerTestRule.getLoggedAtoms()).isEmpty();
+ assertThat(loggerTestRule.getLoggedDownloadReportedAtoms()).isEmpty();
+ verifyWorkLogging(RUN_ATTEMPT_COUNT, WorkResult.SUCCESS_NO_UPDATE_AVAILABLE);
}
@Test
@@ -366,6 +465,15 @@
.thenReturn(Futures.immediateFuture(modelFile));
when(modelDownloader.downloadModel(modelDownloaderDir, MODEL_PROTO_2))
.thenReturn(Futures.immediateFuture(modelFile2));
+ // We assume we always download MODEL_TYPE first and then MODEL_TYPE_2, o/w this will be flaky
+ when(clock.instant())
+ .thenReturn(
+ WORK_STARTED_TIME,
+ DOWNLOAD_STARTED_TIME,
+ DOWNLOAD_ENDED_TIME,
+ DOWNLOAD_STARTED_TIME_2,
+ DOWNLOAD_ENDED_TIME_2,
+ WORK_ENDED_TIME);
ModelDownloadWorker worker = createWorker(RUN_ATTEMPT_COUNT);
assertThat(worker.startWork().get()).isEqualTo(ListenableWorker.Result.success());
@@ -374,6 +482,21 @@
assertThat(downloadedModelManager.listModels(MODEL_TYPE))
.containsExactly(modelFile, modelFile2);
+ List<TextClassifierDownloadReported> atoms = loggerTestRule.getLoggedDownloadReportedAtoms();
+ assertThat(atoms).hasSize(2);
+ assertThat(
+ atoms.stream()
+ .map(TextClassifierDownloadReported::getUrlSuffix)
+ .collect(Collectors.toList()))
+ .containsExactly(MANIFEST_URL, MANIFEST_URL_2);
+ assertThat(atoms.get(0).getDownloadStatus()).isEqualTo(DownloadStatus.SUCCEEDED);
+ assertThat(atoms.get(1).getDownloadStatus()).isEqualTo(DownloadStatus.SUCCEEDED);
+ assertThat(
+ atoms.stream()
+ .map(TextClassifierDownloadReported::getDownloadDurationMillis)
+ .collect(Collectors.toList()))
+ .containsExactly(DOWNLOAD_STARTED_TO_ENDED_MILLIS, DOWNLOAD_STARTED_TO_ENDED_2_MILLIS);
+ verifyWorkLogging(RUN_ATTEMPT_COUNT, WorkResult.SUCCESS_MODEL_DOWNLOADED);
}
@Test
@@ -388,11 +511,15 @@
modelFile.createNewFile();
when(modelDownloader.downloadModel(modelDownloaderDir, MODEL_PROTO))
.thenReturn(Futures.immediateFuture(modelFile));
+ when(clock.instant())
+ .thenReturn(WORK_STARTED_TIME, DOWNLOAD_STARTED_TIME, DOWNLOAD_ENDED_TIME, WORK_ENDED_TIME);
ModelDownloadWorker worker = createWorker(RUN_ATTEMPT_COUNT);
assertThat(worker.startWork().get()).isEqualTo(ListenableWorker.Result.success());
assertThat(modelFile.exists()).isTrue();
assertThat(downloadedModelManager.listModels(MODEL_TYPE)).containsExactly(modelFile);
+ verifySucceededDownloadLogging();
+ verifyWorkLogging(RUN_ATTEMPT_COUNT, WorkResult.SUCCESS_MODEL_DOWNLOADED);
}
@Test
@@ -404,15 +531,18 @@
setUpManifestUrl(MODEL_TYPE, LOCALE_TAG_2, MANIFEST_URL_2);
modelFile.createNewFile();
- downloadedModelManager.registerModel(MODEL_URL, modelFile.getAbsolutePath());
- downloadedModelManager.registerManifest(MANIFEST_URL, MODEL_URL);
- downloadedModelManager.registerManifestEnrollment(MODEL_TYPE, LOCALE_TAG, MANIFEST_URL);
+ when(modelDownloader.downloadManifest(MANIFEST_URL))
+ .thenReturn(Futures.immediateFuture(MODEL_MANIFEST_PROTO));
+ when(modelDownloader.downloadModel(modelDownloaderDir, MODEL_PROTO))
+ .thenReturn(Futures.immediateFuture(modelFile));
- when(modelDownloader.downloadManifest(MANIFEST_URL_2))
- .thenReturn(Futures.immediateFuture(MODEL_MANIFEST_PROTO_2));
modelFile2.createNewFile();
- when(modelDownloader.downloadModel(modelDownloaderDir, MODEL_PROTO_2))
- .thenReturn(Futures.immediateFuture(modelFile2));
+ downloadedModelManager.registerModel(MODEL_URL_2, modelFile2.getAbsolutePath());
+ downloadedModelManager.registerManifest(MANIFEST_URL_2, MODEL_URL_2);
+ downloadedModelManager.registerManifestEnrollment(MODEL_TYPE, LOCALE_TAG_2, MANIFEST_URL_2);
+
+ when(clock.instant())
+ .thenReturn(WORK_STARTED_TIME, DOWNLOAD_STARTED_TIME, DOWNLOAD_ENDED_TIME, WORK_ENDED_TIME);
ModelDownloadWorker worker = createWorker(RUN_ATTEMPT_COUNT);
assertThat(worker.startWork().get()).isEqualTo(ListenableWorker.Result.success());
@@ -420,6 +550,8 @@
assertThat(modelFile2.exists()).isTrue();
assertThat(downloadedModelManager.listModels(MODEL_TYPE))
.containsExactly(modelFile, modelFile2);
+ verifySucceededDownloadLogging();
+ verifyWorkLogging(RUN_ATTEMPT_COUNT, WorkResult.SUCCESS_MODEL_DOWNLOADED);
}
@Test
@@ -440,11 +572,15 @@
.thenReturn(Futures.immediateFuture(modelFile));
when(modelDownloader.downloadModel(modelDownloaderDir, MODEL_PROTO_2))
.thenReturn(Futures.immediateFuture(modelFile2));
+ when(clock.instant())
+ .thenReturn(WORK_STARTED_TIME, DOWNLOAD_STARTED_TIME, DOWNLOAD_ENDED_TIME, WORK_ENDED_TIME);
ModelDownloadWorker worker = createWorker(RUN_ATTEMPT_COUNT);
assertThat(worker.startWork().get()).isEqualTo(ListenableWorker.Result.success());
assertThat(modelFile.exists()).isTrue();
assertThat(downloadedModelManager.listModels(MODEL_TYPE)).containsExactly(modelFile);
+ verifySucceededDownloadLogging();
+ verifyWorkLogging(RUN_ATTEMPT_COUNT, WorkResult.SUCCESS_MODEL_DOWNLOADED);
}
@Test
@@ -462,11 +598,35 @@
modelFile.createNewFile();
when(modelDownloader.downloadModel(modelDownloaderDir, MODEL_PROTO))
.thenReturn(Futures.immediateFuture(modelFile));
+ // We assume we always download MODEL_TYPE first and then MODEL_TYPE_2, o/w this will be flaky
+ when(clock.instant())
+ .thenReturn(
+ WORK_STARTED_TIME,
+ DOWNLOAD_STARTED_TIME,
+ DOWNLOAD_ENDED_TIME,
+ DOWNLOAD_STARTED_TIME_2,
+ DOWNLOAD_ENDED_TIME_2,
+ WORK_ENDED_TIME);
ModelDownloadWorker worker = createWorker(RUN_ATTEMPT_COUNT);
assertThat(worker.startWork().get()).isEqualTo(ListenableWorker.Result.retry());
assertThat(modelFile.exists()).isTrue();
assertThat(downloadedModelManager.listModels(MODEL_TYPE)).containsExactly(modelFile);
+ List<TextClassifierDownloadReported> atoms = loggerTestRule.getLoggedDownloadReportedAtoms();
+ assertThat(atoms).hasSize(2);
+ assertThat(
+ atoms.stream()
+ .map(TextClassifierDownloadReported::getUrlSuffix)
+ .collect(Collectors.toList()))
+ .containsExactly(MANIFEST_URL, MANIFEST_URL_2);
+ assertThat(atoms.get(0).getDownloadStatus()).isEqualTo(DownloadStatus.SUCCEEDED);
+ assertThat(atoms.get(1).getDownloadStatus()).isEqualTo(DownloadStatus.FAILED_AND_RETRY);
+ assertThat(
+ atoms.stream()
+ .map(TextClassifierDownloadReported::getDownloadDurationMillis)
+ .collect(Collectors.toList()))
+ .containsExactly(DOWNLOAD_STARTED_TO_ENDED_MILLIS, DOWNLOAD_STARTED_TO_ENDED_2_MILLIS);
+ verifyWorkLogging(RUN_ATTEMPT_COUNT, WorkResult.RETRY_MODEL_DOWNLOAD_FAILED);
}
@Test
@@ -494,6 +654,15 @@
.thenReturn(Futures.immediateFuture(modelFile2));
when(modelDownloader.downloadModel(modelDownloaderDir, MODEL_PROTO_3))
.thenReturn(Futures.immediateFuture(modelFile3));
+ // We assume we always download MODEL_TYPE first and then MODEL_TYPE_2, o/w this will be flaky
+ when(clock.instant())
+ .thenReturn(
+ WORK_STARTED_TIME,
+ DOWNLOAD_STARTED_TIME,
+ DOWNLOAD_ENDED_TIME,
+ DOWNLOAD_STARTED_TIME_2,
+ DOWNLOAD_ENDED_TIME_2,
+ WORK_ENDED_TIME);
ModelDownloadWorker worker = createWorker(RUN_ATTEMPT_COUNT);
assertThat(worker.startWork().get()).isEqualTo(ListenableWorker.Result.success());
@@ -501,17 +670,32 @@
assertThat(modelFile2.exists()).isTrue();
assertThat(downloadedModelManager.listModels(MODEL_TYPE))
.containsExactly(modelFile, modelFile2);
+ List<TextClassifierDownloadReported> atoms = loggerTestRule.getLoggedDownloadReportedAtoms();
+ assertThat(atoms).hasSize(2);
+ assertThat(
+ atoms.stream()
+ .map(TextClassifierDownloadReported::getUrlSuffix)
+ .collect(Collectors.toList()))
+ .containsExactly(MANIFEST_URL, MANIFEST_URL_2);
+ assertThat(atoms.get(0).getDownloadStatus()).isEqualTo(DownloadStatus.SUCCEEDED);
+ assertThat(atoms.get(1).getDownloadStatus()).isEqualTo(DownloadStatus.SUCCEEDED);
+ assertThat(
+ atoms.stream()
+ .map(TextClassifierDownloadReported::getDownloadDurationMillis)
+ .collect(Collectors.toList()))
+ .containsExactly(DOWNLOAD_STARTED_TO_ENDED_MILLIS, DOWNLOAD_STARTED_TO_ENDED_2_MILLIS);
+ verifyWorkLogging(RUN_ATTEMPT_COUNT, WorkResult.SUCCESS_MODEL_DOWNLOADED);
}
@Test
- public void downloadSucceed_multiLanguageSupportEnabled_checkDownloadedModelTypes()
+ public void downloadSucceed_multiLanguageSupportEnabled_onlyDownloadMultipleForEnabledModelType()
throws Exception {
setDefaultLocalesRule.set(LOCALE_LIST_2);
deviceConfig.setConfig(TextClassifierSettings.MULTI_LANGUAGE_SUPPORT_ENABLED, true);
deviceConfig.setConfig(
- TextClassifierSettings.ENABLED_MODEL_TYPES_FOR_MULTI_LANGUAGE_SUPPORT, MODEL_TYPE);
- setUpManifestUrl(MODEL_TYPE_2, LOCALE_TAG, MANIFEST_URL);
- setUpManifestUrl(MODEL_TYPE_2, LOCALE_TAG_2, MANIFEST_URL_2);
+ TextClassifierSettings.ENABLED_MODEL_TYPES_FOR_MULTI_LANGUAGE_SUPPORT, MODEL_TYPE_2);
+ setUpManifestUrl(MODEL_TYPE, LOCALE_TAG, MANIFEST_URL);
+ setUpManifestUrl(MODEL_TYPE, LOCALE_TAG_2, MANIFEST_URL_2);
when(modelDownloader.downloadManifest(MANIFEST_URL))
.thenReturn(Futures.immediateFuture(MODEL_MANIFEST_PROTO));
@@ -525,11 +709,15 @@
.thenReturn(Futures.immediateFuture(modelFile));
when(modelDownloader.downloadModel(modelDownloaderDir, MODEL_PROTO_2))
.thenReturn(Futures.immediateFuture(modelFile2));
+ when(clock.instant())
+ .thenReturn(WORK_STARTED_TIME, DOWNLOAD_STARTED_TIME, DOWNLOAD_ENDED_TIME, WORK_ENDED_TIME);
ModelDownloadWorker worker = createWorker(RUN_ATTEMPT_COUNT);
assertThat(worker.startWork().get()).isEqualTo(ListenableWorker.Result.success());
assertThat(modelFile.exists()).isTrue();
- assertThat(downloadedModelManager.listModels(MODEL_TYPE_2)).containsExactly(modelFile);
+ assertThat(downloadedModelManager.listModels(MODEL_TYPE)).containsExactly(modelFile);
+ verifySucceededDownloadLogging();
+ verifyWorkLogging(RUN_ATTEMPT_COUNT, WorkResult.SUCCESS_MODEL_DOWNLOADED);
}
private ModelDownloadWorker createWorker(int runAttemptCount) {
@@ -547,23 +735,50 @@
MoreExecutors.newDirectExecutorService(),
modelDownloader,
downloadedModelManager,
- settings);
+ settings,
+ WORK_ID,
+ clock,
+ WORK_SCHEDULED_TIME.toEpochMilli());
}
})
.build();
}
- private void verifyLoggedAtom(
- DownloadStatus downloadStatus, long runAttemptCount, FailureReason failureReason)
- throws Exception {
- TextClassifierDownloadReported atom = Iterables.getOnlyElement(loggerTestRule.getLoggedAtoms());
- assertThat(atom.getDownloadStatus()).isEqualTo(downloadStatus);
+ private void verifySucceededDownloadLogging() throws Exception {
+ TextClassifierDownloadReported atom =
+ Iterables.getOnlyElement(loggerTestRule.getLoggedDownloadReportedAtoms());
+ assertThat(atom.getWorkId()).isEqualTo(WORK_ID);
+ assertThat(atom.getDownloadStatus()).isEqualTo(DownloadStatus.SUCCEEDED);
assertThat(atom.getModelType()).isEqualTo(MODEL_TYPE_ATOM);
assertThat(atom.getUrlSuffix()).isEqualTo(MANIFEST_URL);
- assertThat(atom.getRunAttemptCount()).isEqualTo(runAttemptCount);
- if (failureReason != null) {
- assertThat(atom.getFailureReason()).isEqualTo(failureReason);
- }
+ assertThat(atom.getRunAttemptCount()).isEqualTo(RUN_ATTEMPT_COUNT);
+ assertThat(atom.getFailureReason()).isEqualTo(FailureReason.UNKNOWN_FAILURE_REASON);
+ assertThat(atom.getDownloadDurationMillis()).isEqualTo(DOWNLOAD_STARTED_TO_ENDED_MILLIS);
+ }
+
+ private void verifyFailedDownloadLogging() throws Exception {
+ TextClassifierDownloadReported atom =
+ Iterables.getOnlyElement(loggerTestRule.getLoggedDownloadReportedAtoms());
+ assertThat(atom.getWorkId()).isEqualTo(WORK_ID);
+ assertThat(atom.getDownloadStatus()).isEqualTo(DownloadStatus.FAILED_AND_RETRY);
+ assertThat(atom.getModelType()).isEqualTo(MODEL_TYPE_ATOM);
+ assertThat(atom.getUrlSuffix()).isEqualTo(MANIFEST_URL);
+ assertThat(atom.getRunAttemptCount()).isEqualTo(RUN_ATTEMPT_COUNT);
+ assertThat(atom.getFailureReason()).isEqualTo(FAILED_TO_DOWNLOAD_FAILURE_REASON);
+ assertThat(atom.getDownloaderLibFailureCode())
+ .isEqualTo(ModelDownloadException.DEFAULT_DOWNLOADER_LIB_ERROR_CODE);
+ assertThat(atom.getDownloadDurationMillis()).isEqualTo(DOWNLOAD_STARTED_TO_ENDED_MILLIS);
+ }
+
+ private void verifyWorkLogging(int runTimeAttempt, WorkResult workResult) throws Exception {
+ TextClassifierDownloadWorkCompleted atom =
+ Iterables.getOnlyElement(loggerTestRule.getLoggedDownloadWorkCompletedAtoms());
+ assertThat(atom.getWorkId()).isEqualTo(WORK_ID);
+ assertThat(atom.getWorkResult()).isEqualTo(workResult);
+ assertThat(atom.getRunAttemptCount()).isEqualTo(runTimeAttempt);
+ assertThat(atom.getWorkScheduledToStartedDurationMillis())
+ .isEqualTo(WORK_SCHEDULED_TO_STARTED_MILLIS);
+ assertThat(atom.getWorkStartedToEndedDurationMillis()).isEqualTo(WORK_STARTED_TO_ENDED_MILLIS);
}
private void setUpManifestUrl(
diff --git a/java/tests/instrumentation/src/com/android/textclassifier/downloader/ModelDownloaderImplTest.java b/java/tests/instrumentation/src/com/android/textclassifier/downloader/ModelDownloaderImplTest.java
index 47e7fb6..0818057 100644
--- a/java/tests/instrumentation/src/com/android/textclassifier/downloader/ModelDownloaderImplTest.java
+++ b/java/tests/instrumentation/src/com/android/textclassifier/downloader/ModelDownloaderImplTest.java
@@ -86,6 +86,8 @@
ModelDownloadException e = (ModelDownloadException) t.getCause();
assertThat(e.getErrorCode())
.isEqualTo(ModelDownloadException.FAILED_TO_DOWNLOAD_SERVICE_CONN_BROKEN);
+ assertThat(e.getDownloaderLibErrorCode())
+ .isEqualTo(ModelDownloadException.DEFAULT_DOWNLOADER_LIB_ERROR_CODE);
assertThat(TestModelDownloaderService.isBound()).isFalse();
assertThat(TestModelDownloaderService.hasEverBeenBound()).isFalse();
}
@@ -121,6 +123,8 @@
assertThat(t).hasCauseThat().isInstanceOf(ModelDownloadException.class);
ModelDownloadException e = (ModelDownloadException) t.getCause();
assertThat(e.getErrorCode()).isEqualTo(ModelDownloadException.FAILED_TO_DOWNLOAD_OTHER);
+ assertThat(e.getDownloaderLibErrorCode())
+ .isEqualTo(TestModelDownloaderService.DOWNLOADER_LIB_ERROR_CODE);
assertThat(e).hasMessageThat().contains(TestModelDownloaderService.ERROR_MSG);
assertThat(TestModelDownloaderService.getOnUnbindInvokedLatch().await(1L, SECONDS)).isTrue();
assertThat(TestModelDownloaderService.isBound()).isFalse();
@@ -142,6 +146,8 @@
assertThat(t).hasCauseThat().isInstanceOf(ModelDownloadException.class);
ModelDownloadException e = (ModelDownloadException) t.getCause();
assertThat(e.getErrorCode()).isEqualTo(ModelDownloadException.FAILED_TO_PARSE_MANIFEST);
+ assertThat(e.getDownloaderLibErrorCode())
+ .isEqualTo(ModelDownloadException.DEFAULT_DOWNLOADER_LIB_ERROR_CODE);
assertThat(TestModelDownloaderService.getOnUnbindInvokedLatch().await(1L, SECONDS)).isTrue();
assertThat(TestModelDownloaderService.isBound()).isFalse();
assertThat(TestModelDownloaderService.hasEverBeenBound()).isTrue();
@@ -181,6 +187,8 @@
ModelDownloadException e = (ModelDownloadException) t.getCause();
assertThat(e.getErrorCode())
.isEqualTo(ModelDownloadException.FAILED_TO_DOWNLOAD_SERVICE_CONN_BROKEN);
+ assertThat(e.getDownloaderLibErrorCode())
+ .isEqualTo(ModelDownloadException.DEFAULT_DOWNLOADER_LIB_ERROR_CODE);
assertThat(TestModelDownloaderService.isBound()).isFalse();
assertThat(TestModelDownloaderService.hasEverBeenBound()).isFalse();
}
@@ -218,6 +226,8 @@
assertThat(t).hasCauseThat().isInstanceOf(ModelDownloadException.class);
ModelDownloadException e = (ModelDownloadException) t.getCause();
assertThat(e.getErrorCode()).isEqualTo(ModelDownloadException.FAILED_TO_DOWNLOAD_OTHER);
+ assertThat(e.getDownloaderLibErrorCode())
+ .isEqualTo(TestModelDownloaderService.DOWNLOADER_LIB_ERROR_CODE);
assertThat(e).hasMessageThat().contains(TestModelDownloaderService.ERROR_MSG);
assertThat(TestModelDownloaderService.getOnUnbindInvokedLatch().await(1L, SECONDS)).isTrue();
assertThat(TestModelDownloaderService.isBound()).isFalse();
@@ -239,6 +249,8 @@
assertThat(t).hasCauseThat().isInstanceOf(ModelDownloadException.class);
ModelDownloadException e = (ModelDownloadException) t.getCause();
assertThat(e.getErrorCode()).isEqualTo(ModelDownloadException.FAILED_TO_VALIDATE_MODEL);
+ assertThat(e.getDownloaderLibErrorCode())
+ .isEqualTo(ModelDownloadException.DEFAULT_DOWNLOADER_LIB_ERROR_CODE);
assertThat(TestModelDownloaderService.getOnUnbindInvokedLatch().await(1L, SECONDS)).isTrue();
assertThat(TestModelDownloaderService.isBound()).isFalse();
assertThat(TestModelDownloaderService.hasEverBeenBound()).isTrue();
diff --git a/java/tests/instrumentation/src/com/android/textclassifier/downloader/ModelDownloaderServiceImplTest.java b/java/tests/instrumentation/src/com/android/textclassifier/downloader/ModelDownloaderServiceImplTest.java
index fd16943..eac2af3 100644
--- a/java/tests/instrumentation/src/com/android/textclassifier/downloader/ModelDownloaderServiceImplTest.java
+++ b/java/tests/instrumentation/src/com/android/textclassifier/downloader/ModelDownloaderServiceImplTest.java
@@ -22,11 +22,12 @@
import static org.testng.Assert.expectThrows;
import androidx.test.core.app.ApplicationProvider;
-import com.android.textclassifier.downloader.ModelDownloadException.ErrorCode;
import com.google.android.downloader.DownloadConstraints;
import com.google.android.downloader.DownloadRequest;
import com.google.android.downloader.DownloadResult;
import com.google.android.downloader.Downloader;
+import com.google.android.downloader.ErrorDetails;
+import com.google.android.downloader.RequestException;
import com.google.android.downloader.SimpleFileDownloadDestination;
import com.google.common.util.concurrent.FluentFuture;
import com.google.common.util.concurrent.Futures;
@@ -47,6 +48,14 @@
private static final long BYTES_WRITTEN = 1L;
private static final String DOWNLOAD_URI =
"https://www.gstatic.com/android/text_classifier/r/v999/en.fb";
+ private static final int DOWNLOADER_LIB_ERROR_CODE = 500;
+ private static final String ERROR_MESSAGE = "err_msg";
+ private static final Exception DOWNLOADER_LIB_EXCEPTION =
+ new RequestException(
+ ErrorDetails.builder()
+ .setErrorMessage(ERROR_MESSAGE)
+ .setHttpStatusCode(DOWNLOADER_LIB_ERROR_CODE)
+ .build());
@Mock private Downloader downloader;
private File targetModelFile;
@@ -98,7 +107,7 @@
targetModelFile.createNewFile();
targetMetadataFile.createNewFile();
when(downloader.execute(any()))
- .thenReturn(FluentFuture.from(Futures.immediateFailedFuture(new Exception("err_msg"))));
+ .thenReturn(FluentFuture.from(Futures.immediateFailedFuture(DOWNLOADER_LIB_EXCEPTION)));
modelDownloaderServiceImpl.download(
DOWNLOAD_URI, targetModelFile.getAbsolutePath(), successCallback);
@@ -107,7 +116,8 @@
assertThat(t).hasCauseThat().isInstanceOf(ModelDownloadException.class);
ModelDownloadException e = (ModelDownloadException) t.getCause();
assertThat(e.getErrorCode()).isEqualTo(ModelDownloadException.FAILED_TO_DOWNLOAD_OTHER);
- assertThat(e).hasMessageThat().contains("err_msg");
+ assertThat(e.getDownloaderLibErrorCode()).isEqualTo(DOWNLOADER_LIB_ERROR_CODE);
+ assertThat(e).hasMessageThat().contains(ERROR_MESSAGE);
assertThat(targetModelFile.exists()).isFalse();
assertThat(targetMetadataFile.exists()).isFalse();
}
@@ -132,7 +142,7 @@
targetModelFile.createNewFile();
targetMetadataFile.createNewFile();
when(downloader.execute(any()))
- .thenReturn(FluentFuture.from(Futures.immediateFailedFuture(new Exception("err_msg"))));
+ .thenReturn(FluentFuture.from(Futures.immediateFailedFuture(DOWNLOADER_LIB_EXCEPTION)));
modelDownloaderServiceImpl.download(
DOWNLOAD_URI, targetModelFile.getAbsolutePath(), failureCallback);
@@ -155,8 +165,10 @@
}
@Override
- public void onFailure(@ErrorCode int errorCode, String errorMsg) {
- bytesWrittenFuture.setException(new ModelDownloadException(errorCode, errorMsg));
+ public void onFailure(int downloaderLibErrorCode, String errorMsg) {
+ bytesWrittenFuture.setException(
+ new ModelDownloadException(
+ ModelDownloadException.FAILED_TO_DOWNLOAD_OTHER, downloaderLibErrorCode, errorMsg));
}
}
@@ -171,7 +183,7 @@
}
@Override
- public void onFailure(@ErrorCode int errorCode, String errorMsg) {
+ public void onFailure(int downloaderLibErrorCode, String errorMsg) {
onFailureCalled = true;
throw new RuntimeException();
}
diff --git a/java/tests/instrumentation/src/com/android/textclassifier/downloader/TestModelDownloaderService.java b/java/tests/instrumentation/src/com/android/textclassifier/downloader/TestModelDownloaderService.java
index 0527845..a782b4c 100644
--- a/java/tests/instrumentation/src/com/android/textclassifier/downloader/TestModelDownloaderService.java
+++ b/java/tests/instrumentation/src/com/android/textclassifier/downloader/TestModelDownloaderService.java
@@ -33,7 +33,7 @@
public static final String GOOD_URI = "good_uri";
public static final String BAD_URI = "bad_uri";
public static final long BYTES_WRITTEN = 1L;
- public static final int ERROR_CODE = ModelDownloadException.FAILED_TO_DOWNLOAD_OTHER;
+ public static final int DOWNLOADER_LIB_ERROR_CODE = 500;
public static final String ERROR_MSG = "not good uri";
public enum DownloadResult {
@@ -129,7 +129,7 @@
callback.onSuccess(BYTES_WRITTEN);
break;
case FAILED:
- callback.onFailure(ERROR_CODE, ERROR_MSG);
+ callback.onFailure(DOWNLOADER_LIB_ERROR_CODE, ERROR_MSG);
break;
case DO_NOTHING:
// Do nothing
diff --git a/java/tests/instrumentation/src/com/android/textclassifier/downloader/TextClassifierDownloadLoggerTest.java b/java/tests/instrumentation/src/com/android/textclassifier/downloader/TextClassifierDownloadLoggerTest.java
index f979021..ed76fa8 100644
--- a/java/tests/instrumentation/src/com/android/textclassifier/downloader/TextClassifierDownloadLoggerTest.java
+++ b/java/tests/instrumentation/src/com/android/textclassifier/downloader/TextClassifierDownloadLoggerTest.java
@@ -20,6 +20,8 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.os.AtomsProto.TextClassifierDownloadReported;
+import com.android.os.AtomsProto.TextClassifierDownloadWorkCompleted;
+import com.android.os.AtomsProto.TextClassifierDownloadWorkScheduled;
import com.android.textclassifier.common.ModelType;
import com.android.textclassifier.common.statsd.TextClassifierDownloadLoggerTestRule;
import com.google.common.collect.Iterables;
@@ -38,6 +40,19 @@
private static final TextClassifierDownloadReported.FailureReason FAILURE_REASON_ATOM =
TextClassifierDownloadReported.FailureReason.FAILED_TO_DOWNLOAD_404_ERROR;
private static final int RUN_ATTEMPT_COUNT = 1;
+ private static final long WORK_ID = 123456789L;
+ private static final long DOWNLOAD_DURATION_MILLIS = 666L;
+ private static final int DOWNLOADER_LIB_ERROR_CODE = 500;
+ private static final int REASON_TO_SCHEDULE =
+ TextClassifierDownloadLogger.REASON_TO_SCHEDULE_TCS_STARTED;
+ private static final TextClassifierDownloadWorkScheduled.ReasonToSchedule
+ REASON_TO_SCHEDULE_ATOM = TextClassifierDownloadWorkScheduled.ReasonToSchedule.TCS_STARTED;
+ private static final int WORK_RESULT =
+ TextClassifierDownloadLogger.WORK_RESULT_SUCCESS_MODEL_DOWNLOADED;
+ private static final TextClassifierDownloadWorkCompleted.WorkResult WORK_RESULT_ATOM =
+ TextClassifierDownloadWorkCompleted.WorkResult.SUCCESS_MODEL_DOWNLOADED;
+ private static final long SCHEDULED_TO_START_DURATION_MILLIS = 777L;
+ private static final long STARTED_TO_FINISHED_DURATION_MILLIS = 888L;
@Rule
public final TextClassifierDownloadLoggerTestRule loggerTestRule =
@@ -45,27 +60,85 @@
@Test
public void downloadSucceeded() throws Exception {
- TextClassifierDownloadLogger.downloadSucceeded(MODEL_TYPE, URL, RUN_ATTEMPT_COUNT);
+ TextClassifierDownloadLogger.downloadSucceeded(
+ WORK_ID, MODEL_TYPE, URL, RUN_ATTEMPT_COUNT, DOWNLOAD_DURATION_MILLIS);
- TextClassifierDownloadReported atom = Iterables.getOnlyElement(loggerTestRule.getLoggedAtoms());
+ TextClassifierDownloadReported atom =
+ Iterables.getOnlyElement(loggerTestRule.getLoggedDownloadReportedAtoms());
+ assertThat(atom.getWorkId()).isEqualTo(WORK_ID);
assertThat(atom.getDownloadStatus())
.isEqualTo(TextClassifierDownloadReported.DownloadStatus.SUCCEEDED);
assertThat(atom.getModelType()).isEqualTo(MODEL_TYPE_ATOM);
assertThat(atom.getUrlSuffix()).isEqualTo(URL);
assertThat(atom.getRunAttemptCount()).isEqualTo(RUN_ATTEMPT_COUNT);
+ assertThat(atom.getDownloadDurationMillis()).isEqualTo(DOWNLOAD_DURATION_MILLIS);
}
@Test
- public void downloadFailedAndRetry() throws Exception {
- TextClassifierDownloadLogger.downloadFailedAndRetry(
- MODEL_TYPE, URL, ERROR_CODE, RUN_ATTEMPT_COUNT);
+ public void downloadFailed() throws Exception {
+ TextClassifierDownloadLogger.downloadFailed(
+ WORK_ID,
+ MODEL_TYPE,
+ URL,
+ ERROR_CODE,
+ RUN_ATTEMPT_COUNT,
+ DOWNLOADER_LIB_ERROR_CODE,
+ DOWNLOAD_DURATION_MILLIS);
- TextClassifierDownloadReported atom = Iterables.getOnlyElement(loggerTestRule.getLoggedAtoms());
+ TextClassifierDownloadReported atom =
+ Iterables.getOnlyElement(loggerTestRule.getLoggedDownloadReportedAtoms());
+ assertThat(atom.getWorkId()).isEqualTo(WORK_ID);
assertThat(atom.getDownloadStatus())
.isEqualTo(TextClassifierDownloadReported.DownloadStatus.FAILED_AND_RETRY);
assertThat(atom.getModelType()).isEqualTo(MODEL_TYPE_ATOM);
assertThat(atom.getUrlSuffix()).isEqualTo(URL);
assertThat(atom.getRunAttemptCount()).isEqualTo(RUN_ATTEMPT_COUNT);
assertThat(atom.getFailureReason()).isEqualTo(FAILURE_REASON_ATOM);
+ assertThat(atom.getDownloaderLibFailureCode()).isEqualTo(DOWNLOADER_LIB_ERROR_CODE);
+ assertThat(atom.getDownloadDurationMillis()).isEqualTo(DOWNLOAD_DURATION_MILLIS);
+ }
+
+ @Test
+ public void downloadWorkScheduled_succeeded() throws Exception {
+ TextClassifierDownloadLogger.downloadWorkScheduled(
+ WORK_ID, REASON_TO_SCHEDULE, /* failedToSchedule= */ false);
+
+ TextClassifierDownloadWorkScheduled atom =
+ Iterables.getOnlyElement(loggerTestRule.getLoggedDownloadWorkScheduledAtoms());
+ assertThat(atom.getWorkId()).isEqualTo(WORK_ID);
+ assertThat(atom.getReasonToSchedule()).isEqualTo(REASON_TO_SCHEDULE_ATOM);
+ assertThat(atom.getFailedToSchedule()).isFalse();
+ }
+
+ @Test
+ public void downloadWorkScheduled_failed() throws Exception {
+ TextClassifierDownloadLogger.downloadWorkScheduled(
+ WORK_ID, REASON_TO_SCHEDULE, /* failedToSchedule= */ true);
+
+ TextClassifierDownloadWorkScheduled atom =
+ Iterables.getOnlyElement(loggerTestRule.getLoggedDownloadWorkScheduledAtoms());
+ assertThat(atom.getWorkId()).isEqualTo(WORK_ID);
+ assertThat(atom.getReasonToSchedule()).isEqualTo(REASON_TO_SCHEDULE_ATOM);
+ assertThat(atom.getFailedToSchedule()).isTrue();
+ }
+
+ @Test
+ public void downloadWorkCompleted() throws Exception {
+ TextClassifierDownloadLogger.downloadWorkCompleted(
+ WORK_ID,
+ WORK_RESULT,
+ RUN_ATTEMPT_COUNT,
+ SCHEDULED_TO_START_DURATION_MILLIS,
+ STARTED_TO_FINISHED_DURATION_MILLIS);
+
+ TextClassifierDownloadWorkCompleted atom =
+ Iterables.getOnlyElement(loggerTestRule.getLoggedDownloadWorkCompletedAtoms());
+ assertThat(atom.getWorkId()).isEqualTo(WORK_ID);
+ assertThat(atom.getWorkResult()).isEqualTo(WORK_RESULT_ATOM);
+ assertThat(atom.getRunAttemptCount()).isEqualTo(RUN_ATTEMPT_COUNT);
+ assertThat(atom.getWorkScheduledToStartedDurationMillis())
+ .isEqualTo(SCHEDULED_TO_START_DURATION_MILLIS);
+ assertThat(atom.getWorkStartedToEndedDurationMillis())
+ .isEqualTo(STARTED_TO_FINISHED_DURATION_MILLIS);
}
}
diff --git a/native/actions/test_data/actions_suggestions_grammar_test.model b/native/actions/test_data/actions_suggestions_grammar_test.model
index d96eef4..77e556c 100644
--- a/native/actions/test_data/actions_suggestions_grammar_test.model
+++ b/native/actions/test_data/actions_suggestions_grammar_test.model
Binary files differ
diff --git a/native/actions/test_data/actions_suggestions_test.model b/native/actions/test_data/actions_suggestions_test.model
index deb284e..c468bd5 100644
--- a/native/actions/test_data/actions_suggestions_test.model
+++ b/native/actions/test_data/actions_suggestions_test.model
Binary files differ
diff --git a/native/actions/test_data/actions_suggestions_test.multi_task_9heads.model b/native/actions/test_data/actions_suggestions_test.multi_task_9heads.model
index 766d5b0..ec421a1 100644
--- a/native/actions/test_data/actions_suggestions_test.multi_task_9heads.model
+++ b/native/actions/test_data/actions_suggestions_test.multi_task_9heads.model
Binary files differ
diff --git a/native/actions/test_data/actions_suggestions_test.multi_task_sr_emoji.model b/native/actions/test_data/actions_suggestions_test.multi_task_sr_emoji.model
index bde92c2..24be6c6 100644
--- a/native/actions/test_data/actions_suggestions_test.multi_task_sr_emoji.model
+++ b/native/actions/test_data/actions_suggestions_test.multi_task_sr_emoji.model
Binary files differ
diff --git a/native/actions/test_data/actions_suggestions_test.multi_task_sr_nudge_signal_v0.model b/native/actions/test_data/actions_suggestions_test.multi_task_sr_nudge_signal_v0.model
index 1ea71b4..fd7ddf2 100644
--- a/native/actions/test_data/actions_suggestions_test.multi_task_sr_nudge_signal_v0.model
+++ b/native/actions/test_data/actions_suggestions_test.multi_task_sr_nudge_signal_v0.model
Binary files differ
diff --git a/native/actions/test_data/actions_suggestions_test.multi_task_sr_p13n.model b/native/actions/test_data/actions_suggestions_test.multi_task_sr_p13n.model
index 336a0c3..c969c56 100644
--- a/native/actions/test_data/actions_suggestions_test.multi_task_sr_p13n.model
+++ b/native/actions/test_data/actions_suggestions_test.multi_task_sr_p13n.model
Binary files differ
diff --git a/native/actions/test_data/actions_suggestions_test.multi_task_tf2_test.model b/native/actions/test_data/actions_suggestions_test.multi_task_tf2_test.model
index 9ba5574..d171898 100644
--- a/native/actions/test_data/actions_suggestions_test.multi_task_tf2_test.model
+++ b/native/actions/test_data/actions_suggestions_test.multi_task_tf2_test.model
Binary files differ
diff --git a/native/actions/test_data/actions_suggestions_test.sensitive_tflite.model b/native/actions/test_data/actions_suggestions_test.sensitive_tflite.model
index bc9cd04..937552b 100644
--- a/native/actions/test_data/actions_suggestions_test.sensitive_tflite.model
+++ b/native/actions/test_data/actions_suggestions_test.sensitive_tflite.model
Binary files differ
diff --git a/native/lang_id/script/approx-script-data.cc b/native/lang_id/script/approx-script-data.cc
index 233653f..678a1e9 100755
--- a/native/lang_id/script/approx-script-data.cc
+++ b/native/lang_id/script/approx-script-data.cc
@@ -27,7 +27,7 @@
namespace mobile {
namespace approx_script_internal {
-const int kNumRanges = 376;
+const int kNumRanges = 389;
const uint32 kRangeFirst[] = {
65, // Range #0: [65, 90, Latin]
@@ -67,8 +67,8 @@
2048, // Range #34: [2048, 2110, Samaritan]
2112, // Range #35: [2112, 2142, Mandaic]
2144, // Range #36: [2144, 2154, Syriac]
- 2208, // Range #37: [2208, 2247, Arabic]
- 2259, // Range #38: [2259, 2273, Arabic]
+ 2160, // Range #37: [2160, 2193, Arabic]
+ 2200, // Range #38: [2200, 2273, Arabic]
2275, // Range #39: [2275, 2303, Arabic]
2304, // Range #40: [2304, 2384, Devanagari]
2389, // Range #41: [2389, 2403, Devanagari]
@@ -87,32 +87,32 @@
3031, // Range #54: [3031, 3031, Tamil]
3046, // Range #55: [3046, 3066, Tamil]
3072, // Range #56: [3072, 3149, Telugu]
- 3157, // Range #57: [3157, 3162, Telugu]
- 3168, // Range #58: [3168, 3183, Telugu]
- 3191, // Range #59: [3191, 3199, Telugu]
- 3200, // Range #60: [3200, 3277, Kannada]
- 3285, // Range #61: [3285, 3286, Kannada]
- 3294, // Range #62: [3294, 3314, Kannada]
- 3328, // Range #63: [3328, 3455, Malayalam]
- 3457, // Range #64: [3457, 3551, Sinhala]
- 3558, // Range #65: [3558, 3572, Sinhala]
- 3585, // Range #66: [3585, 3642, Thai]
- 3648, // Range #67: [3648, 3675, Thai]
- 3713, // Range #68: [3713, 3807, Lao]
- 3840, // Range #69: [3840, 4052, Tibetan]
- 4057, // Range #70: [4057, 4058, Tibetan]
- 4096, // Range #71: [4096, 4255, Myanmar]
- 4256, // Range #72: [4256, 4295, Georgian]
- 4301, // Range #73: [4301, 4346, Georgian]
- 4348, // Range #74: [4348, 4351, Georgian]
- 4352, // Range #75: [4352, 4607, Hangul]
- 4608, // Range #76: [4608, 5017, Ethiopic]
- 5024, // Range #77: [5024, 5117, Cherokee]
- 5120, // Range #78: [5120, 5759, Canadian_Aboriginal]
- 5760, // Range #79: [5760, 5788, Ogham]
- 5792, // Range #80: [5792, 5866, Runic]
- 5870, // Range #81: [5870, 5880, Runic]
- 5888, // Range #82: [5888, 5908, Tagalog]
+ 3157, // Range #57: [3157, 3183, Telugu]
+ 3191, // Range #58: [3191, 3199, Telugu]
+ 3200, // Range #59: [3200, 3277, Kannada]
+ 3285, // Range #60: [3285, 3286, Kannada]
+ 3293, // Range #61: [3293, 3314, Kannada]
+ 3328, // Range #62: [3328, 3455, Malayalam]
+ 3457, // Range #63: [3457, 3551, Sinhala]
+ 3558, // Range #64: [3558, 3572, Sinhala]
+ 3585, // Range #65: [3585, 3642, Thai]
+ 3648, // Range #66: [3648, 3675, Thai]
+ 3713, // Range #67: [3713, 3807, Lao]
+ 3840, // Range #68: [3840, 4052, Tibetan]
+ 4057, // Range #69: [4057, 4058, Tibetan]
+ 4096, // Range #70: [4096, 4255, Myanmar]
+ 4256, // Range #71: [4256, 4295, Georgian]
+ 4301, // Range #72: [4301, 4346, Georgian]
+ 4348, // Range #73: [4348, 4351, Georgian]
+ 4352, // Range #74: [4352, 4607, Hangul]
+ 4608, // Range #75: [4608, 5017, Ethiopic]
+ 5024, // Range #76: [5024, 5117, Cherokee]
+ 5120, // Range #77: [5120, 5759, Canadian_Aboriginal]
+ 5760, // Range #78: [5760, 5788, Ogham]
+ 5792, // Range #79: [5792, 5866, Runic]
+ 5870, // Range #80: [5870, 5880, Runic]
+ 5888, // Range #81: [5888, 5909, Tagalog]
+ 5919, // Range #82: [5919, 5919, Tagalog]
5920, // Range #83: [5920, 5940, Hanunoo]
5952, // Range #84: [5952, 5971, Buhid]
5984, // Range #85: [5984, 6003, Tagbanwa]
@@ -133,7 +133,7 @@
6688, // Range #100: [6688, 6793, Tai_Tham]
6800, // Range #101: [6800, 6809, Tai_Tham]
6816, // Range #102: [6816, 6829, Tai_Tham]
- 6912, // Range #103: [6912, 7036, Balinese]
+ 6912, // Range #103: [6912, 7038, Balinese]
7040, // Range #104: [7040, 7103, Sundanese]
7104, // Range #105: [7104, 7155, Batak]
7164, // Range #106: [7164, 7167, Batak]
@@ -164,7 +164,7 @@
8526, // Range #131: [8526, 8526, Latin]
8544, // Range #132: [8544, 8584, Latin]
10240, // Range #133: [10240, 10495, Braille]
- 11264, // Range #134: [11264, 11358, Glagolitic]
+ 11264, // Range #134: [11264, 11359, Glagolitic]
11360, // Range #135: [11360, 11391, Latin]
11392, // Range #136: [11392, 11507, Coptic]
11513, // Range #137: [11513, 11519, Coptic]
@@ -196,7 +196,7 @@
13008, // Range #163: [13008, 13054, Katakana]
13056, // Range #164: [13056, 13143, Katakana]
13312, // Range #165: [13312, 19903, Han]
- 19968, // Range #166: [19968, 40956, Han]
+ 19968, // Range #166: [19968, 40959, Han]
40960, // Range #167: [40960, 42182, Yi]
42192, // Range #168: [42192, 42239, Lisu]
42240, // Range #169: [42240, 42539, Vai]
@@ -204,208 +204,221 @@
42656, // Range #171: [42656, 42743, Bamum]
42786, // Range #172: [42786, 42887, Latin]
42891, // Range #173: [42891, 42954, Latin]
- 42997, // Range #174: [42997, 43007, Latin]
- 43008, // Range #175: [43008, 43052, Syloti_Nagri]
- 43072, // Range #176: [43072, 43127, Phags_Pa]
- 43136, // Range #177: [43136, 43205, Saurashtra]
- 43214, // Range #178: [43214, 43225, Saurashtra]
- 43232, // Range #179: [43232, 43263, Devanagari]
- 43264, // Range #180: [43264, 43309, Kayah_Li]
- 43311, // Range #181: [43311, 43311, Kayah_Li]
- 43312, // Range #182: [43312, 43347, Rejang]
- 43359, // Range #183: [43359, 43359, Rejang]
- 43360, // Range #184: [43360, 43388, Hangul]
- 43392, // Range #185: [43392, 43469, Javanese]
- 43472, // Range #186: [43472, 43487, Javanese]
- 43488, // Range #187: [43488, 43518, Myanmar]
- 43520, // Range #188: [43520, 43574, Cham]
- 43584, // Range #189: [43584, 43615, Cham]
- 43616, // Range #190: [43616, 43647, Myanmar]
- 43648, // Range #191: [43648, 43714, Tai_Viet]
- 43739, // Range #192: [43739, 43743, Tai_Viet]
- 43744, // Range #193: [43744, 43766, Meetei_Mayek]
- 43777, // Range #194: [43777, 43798, Ethiopic]
- 43808, // Range #195: [43808, 43822, Ethiopic]
- 43824, // Range #196: [43824, 43866, Latin]
- 43868, // Range #197: [43868, 43876, Latin]
- 43877, // Range #198: [43877, 43877, Greek]
- 43878, // Range #199: [43878, 43881, Latin]
- 43888, // Range #200: [43888, 43967, Cherokee]
- 43968, // Range #201: [43968, 44025, Meetei_Mayek]
- 44032, // Range #202: [44032, 55203, Hangul]
- 55216, // Range #203: [55216, 55291, Hangul]
- 63744, // Range #204: [63744, 64217, Han]
- 64256, // Range #205: [64256, 64262, Latin]
- 64275, // Range #206: [64275, 64279, Armenian]
- 64285, // Range #207: [64285, 64335, Hebrew]
- 64336, // Range #208: [64336, 64449, Arabic]
- 64467, // Range #209: [64467, 64829, Arabic]
- 64848, // Range #210: [64848, 64967, Arabic]
- 65008, // Range #211: [65008, 65021, Arabic]
- 65070, // Range #212: [65070, 65071, Cyrillic]
- 65136, // Range #213: [65136, 65276, Arabic]
- 65313, // Range #214: [65313, 65338, Latin]
- 65345, // Range #215: [65345, 65370, Latin]
- 65382, // Range #216: [65382, 65391, Katakana]
- 65393, // Range #217: [65393, 65437, Katakana]
- 65440, // Range #218: [65440, 65500, Hangul]
- 65536, // Range #219: [65536, 65629, Linear_B]
- 65664, // Range #220: [65664, 65786, Linear_B]
- 65856, // Range #221: [65856, 65934, Greek]
- 65952, // Range #222: [65952, 65952, Greek]
- 66176, // Range #223: [66176, 66204, Lycian]
- 66208, // Range #224: [66208, 66256, Carian]
- 66304, // Range #225: [66304, 66339, Old_Italic]
- 66349, // Range #226: [66349, 66351, Old_Italic]
- 66352, // Range #227: [66352, 66378, Gothic]
- 66384, // Range #228: [66384, 66426, Old_Permic]
- 66432, // Range #229: [66432, 66463, Ugaritic]
- 66464, // Range #230: [66464, 66517, Old_Persian]
- 66560, // Range #231: [66560, 66639, Deseret]
- 66640, // Range #232: [66640, 66687, Shavian]
- 66688, // Range #233: [66688, 66729, Osmanya]
- 66736, // Range #234: [66736, 66811, Osage]
- 66816, // Range #235: [66816, 66855, Elbasan]
- 66864, // Range #236: [66864, 66915, Caucasian_Albanian]
- 66927, // Range #237: [66927, 66927, Caucasian_Albanian]
- 67072, // Range #238: [67072, 67382, Linear_A]
- 67392, // Range #239: [67392, 67413, Linear_A]
- 67424, // Range #240: [67424, 67431, Linear_A]
- 67584, // Range #241: [67584, 67647, Cypriot]
- 67648, // Range #242: [67648, 67679, Imperial_Aramaic]
- 67680, // Range #243: [67680, 67711, Palmyrene]
- 67712, // Range #244: [67712, 67742, Nabataean]
- 67751, // Range #245: [67751, 67759, Nabataean]
- 67808, // Range #246: [67808, 67829, Hatran]
- 67835, // Range #247: [67835, 67839, Hatran]
- 67840, // Range #248: [67840, 67871, Phoenician]
- 67872, // Range #249: [67872, 67897, Lydian]
- 67903, // Range #250: [67903, 67903, Lydian]
- 67968, // Range #251: [67968, 67999, Meroitic_Hieroglyphs]
- 68000, // Range #252: [68000, 68095, Meroitic_Cursive]
- 68096, // Range #253: [68096, 68102, Kharoshthi]
- 68108, // Range #254: [68108, 68168, Kharoshthi]
- 68176, // Range #255: [68176, 68184, Kharoshthi]
- 68192, // Range #256: [68192, 68223, Old_South_Arabian]
- 68224, // Range #257: [68224, 68255, Old_North_Arabian]
- 68288, // Range #258: [68288, 68342, Manichaean]
- 68352, // Range #259: [68352, 68415, Avestan]
- 68416, // Range #260: [68416, 68447, Inscriptional_Parthian]
- 68448, // Range #261: [68448, 68466, Inscriptional_Pahlavi]
- 68472, // Range #262: [68472, 68479, Inscriptional_Pahlavi]
- 68480, // Range #263: [68480, 68497, Psalter_Pahlavi]
- 68505, // Range #264: [68505, 68508, Psalter_Pahlavi]
- 68521, // Range #265: [68521, 68527, Psalter_Pahlavi]
- 68608, // Range #266: [68608, 68680, Old_Turkic]
- 68736, // Range #267: [68736, 68786, Old_Hungarian]
- 68800, // Range #268: [68800, 68850, Old_Hungarian]
- 68858, // Range #269: [68858, 68863, Old_Hungarian]
- 68864, // Range #270: [68864, 68903, Hanifi_Rohingya]
- 68912, // Range #271: [68912, 68921, Hanifi_Rohingya]
- 69216, // Range #272: [69216, 69246, Arabic]
- 69248, // Range #273: [69248, 69297, Yezidi]
- 69376, // Range #274: [69376, 69415, Old_Sogdian]
- 69424, // Range #275: [69424, 69465, Sogdian]
- 69552, // Range #276: [69552, 69579, Chorasmian]
- 69600, // Range #277: [69600, 69622, Elymaic]
- 69632, // Range #278: [69632, 69743, Brahmi]
- 69759, // Range #279: [69759, 69759, Brahmi]
- 69760, // Range #280: [69760, 69825, Kaithi]
- 69837, // Range #281: [69837, 69837, Kaithi]
- 69840, // Range #282: [69840, 69864, Sora_Sompeng]
- 69872, // Range #283: [69872, 69881, Sora_Sompeng]
- 69888, // Range #284: [69888, 69959, Chakma]
- 69968, // Range #285: [69968, 70006, Mahajani]
- 70016, // Range #286: [70016, 70111, Sharada]
- 70113, // Range #287: [70113, 70132, Sinhala]
- 70144, // Range #288: [70144, 70206, Khojki]
- 70272, // Range #289: [70272, 70313, Multani]
- 70320, // Range #290: [70320, 70378, Khudawadi]
- 70384, // Range #291: [70384, 70393, Khudawadi]
- 70400, // Range #292: [70400, 70457, Grantha]
- 70460, // Range #293: [70460, 70480, Grantha]
- 70487, // Range #294: [70487, 70487, Grantha]
- 70493, // Range #295: [70493, 70516, Grantha]
- 70656, // Range #296: [70656, 70753, Newa]
- 70784, // Range #297: [70784, 70855, Tirhuta]
- 70864, // Range #298: [70864, 70873, Tirhuta]
- 71040, // Range #299: [71040, 71133, Siddham]
- 71168, // Range #300: [71168, 71236, Modi]
- 71248, // Range #301: [71248, 71257, Modi]
- 71264, // Range #302: [71264, 71276, Mongolian]
- 71296, // Range #303: [71296, 71352, Takri]
- 71360, // Range #304: [71360, 71369, Takri]
- 71424, // Range #305: [71424, 71487, Ahom]
- 71680, // Range #306: [71680, 71739, Dogra]
- 71840, // Range #307: [71840, 71922, Warang_Citi]
- 71935, // Range #308: [71935, 71935, Warang_Citi]
- 71936, // Range #309: [71936, 72006, Dives_Akuru]
- 72016, // Range #310: [72016, 72025, Dives_Akuru]
- 72096, // Range #311: [72096, 72164, Nandinagari]
- 72192, // Range #312: [72192, 72263, Zanabazar_Square]
- 72272, // Range #313: [72272, 72354, Soyombo]
- 72384, // Range #314: [72384, 72440, Pau_Cin_Hau]
- 72704, // Range #315: [72704, 72773, Bhaiksuki]
- 72784, // Range #316: [72784, 72812, Bhaiksuki]
- 72816, // Range #317: [72816, 72886, Marchen]
- 72960, // Range #318: [72960, 73031, Masaram_Gondi]
- 73040, // Range #319: [73040, 73049, Masaram_Gondi]
- 73056, // Range #320: [73056, 73112, Gunjala_Gondi]
- 73120, // Range #321: [73120, 73129, Gunjala_Gondi]
- 73440, // Range #322: [73440, 73464, Makasar]
- 73648, // Range #323: [73648, 73648, Lisu]
- 73664, // Range #324: [73664, 73713, Tamil]
- 73727, // Range #325: [73727, 73727, Tamil]
- 73728, // Range #326: [73728, 74649, Cuneiform]
- 74752, // Range #327: [74752, 74868, Cuneiform]
- 74880, // Range #328: [74880, 75075, Cuneiform]
- 77824, // Range #329: [77824, 78904, Egyptian_Hieroglyphs]
- 82944, // Range #330: [82944, 83526, Anatolian_Hieroglyphs]
- 92160, // Range #331: [92160, 92728, Bamum]
- 92736, // Range #332: [92736, 92783, Mro]
- 92880, // Range #333: [92880, 92917, Bassa_Vah]
- 92928, // Range #334: [92928, 92997, Pahawh_Hmong]
- 93008, // Range #335: [93008, 93047, Pahawh_Hmong]
- 93053, // Range #336: [93053, 93071, Pahawh_Hmong]
- 93760, // Range #337: [93760, 93850, Medefaidrin]
- 93952, // Range #338: [93952, 94087, Miao]
- 94095, // Range #339: [94095, 94111, Miao]
- 94176, // Range #340: [94176, 94176, Tangut]
- 94177, // Range #341: [94177, 94177, Nushu]
- 94180, // Range #342: [94180, 94180, Khitan_Small_Script]
- 94192, // Range #343: [94192, 94193, Han]
- 94208, // Range #344: [94208, 100343, Tangut]
- 100352, // Range #345: [100352, 101119, Tangut]
- 101120, // Range #346: [101120, 101589, Khitan_Small_Script]
- 101632, // Range #347: [101632, 101640, Tangut]
- 110592, // Range #348: [110592, 110592, Katakana]
- 110593, // Range #349: [110593, 110878, Hiragana]
- 110928, // Range #350: [110928, 110930, Hiragana]
- 110948, // Range #351: [110948, 110951, Katakana]
- 110960, // Range #352: [110960, 111355, Nushu]
- 113664, // Range #353: [113664, 113770, Duployan]
- 113776, // Range #354: [113776, 113800, Duployan]
- 113808, // Range #355: [113808, 113823, Duployan]
- 119296, // Range #356: [119296, 119365, Greek]
- 120832, // Range #357: [120832, 121483, SignWriting]
- 121499, // Range #358: [121499, 121519, SignWriting]
- 122880, // Range #359: [122880, 122922, Glagolitic]
- 123136, // Range #360: [123136, 123215, Nyiakeng_Puachue_Hmong]
- 123584, // Range #361: [123584, 123641, Wancho]
- 123647, // Range #362: [123647, 123647, Wancho]
- 124928, // Range #363: [124928, 125142, Mende_Kikakui]
- 125184, // Range #364: [125184, 125279, Adlam]
- 126464, // Range #365: [126464, 126523, Arabic]
- 126530, // Range #366: [126530, 126619, Arabic]
- 126625, // Range #367: [126625, 126651, Arabic]
- 126704, // Range #368: [126704, 126705, Arabic]
- 127488, // Range #369: [127488, 127488, Hiragana]
- 131072, // Range #370: [131072, 173789, Han]
- 173824, // Range #371: [173824, 177972, Han]
- 177984, // Range #372: [177984, 183969, Han]
- 183984, // Range #373: [183984, 191456, Han]
- 194560, // Range #374: [194560, 195101, Han]
- 196608, // Range #375: [196608, 201546, Han]
+ 42960, // Range #174: [42960, 42969, Latin]
+ 42994, // Range #175: [42994, 43007, Latin]
+ 43008, // Range #176: [43008, 43052, Syloti_Nagri]
+ 43072, // Range #177: [43072, 43127, Phags_Pa]
+ 43136, // Range #178: [43136, 43205, Saurashtra]
+ 43214, // Range #179: [43214, 43225, Saurashtra]
+ 43232, // Range #180: [43232, 43263, Devanagari]
+ 43264, // Range #181: [43264, 43309, Kayah_Li]
+ 43311, // Range #182: [43311, 43311, Kayah_Li]
+ 43312, // Range #183: [43312, 43347, Rejang]
+ 43359, // Range #184: [43359, 43359, Rejang]
+ 43360, // Range #185: [43360, 43388, Hangul]
+ 43392, // Range #186: [43392, 43469, Javanese]
+ 43472, // Range #187: [43472, 43487, Javanese]
+ 43488, // Range #188: [43488, 43518, Myanmar]
+ 43520, // Range #189: [43520, 43574, Cham]
+ 43584, // Range #190: [43584, 43615, Cham]
+ 43616, // Range #191: [43616, 43647, Myanmar]
+ 43648, // Range #192: [43648, 43714, Tai_Viet]
+ 43739, // Range #193: [43739, 43743, Tai_Viet]
+ 43744, // Range #194: [43744, 43766, Meetei_Mayek]
+ 43777, // Range #195: [43777, 43798, Ethiopic]
+ 43808, // Range #196: [43808, 43822, Ethiopic]
+ 43824, // Range #197: [43824, 43866, Latin]
+ 43868, // Range #198: [43868, 43876, Latin]
+ 43877, // Range #199: [43877, 43877, Greek]
+ 43878, // Range #200: [43878, 43881, Latin]
+ 43888, // Range #201: [43888, 43967, Cherokee]
+ 43968, // Range #202: [43968, 44025, Meetei_Mayek]
+ 44032, // Range #203: [44032, 55203, Hangul]
+ 55216, // Range #204: [55216, 55291, Hangul]
+ 63744, // Range #205: [63744, 64217, Han]
+ 64256, // Range #206: [64256, 64262, Latin]
+ 64275, // Range #207: [64275, 64279, Armenian]
+ 64285, // Range #208: [64285, 64335, Hebrew]
+ 64336, // Range #209: [64336, 64450, Arabic]
+ 64467, // Range #210: [64467, 64829, Arabic]
+ 64832, // Range #211: [64832, 64967, Arabic]
+ 64975, // Range #212: [64975, 64975, Arabic]
+ 65008, // Range #213: [65008, 65023, Arabic]
+ 65070, // Range #214: [65070, 65071, Cyrillic]
+ 65136, // Range #215: [65136, 65276, Arabic]
+ 65313, // Range #216: [65313, 65338, Latin]
+ 65345, // Range #217: [65345, 65370, Latin]
+ 65382, // Range #218: [65382, 65391, Katakana]
+ 65393, // Range #219: [65393, 65437, Katakana]
+ 65440, // Range #220: [65440, 65500, Hangul]
+ 65536, // Range #221: [65536, 65629, Linear_B]
+ 65664, // Range #222: [65664, 65786, Linear_B]
+ 65856, // Range #223: [65856, 65934, Greek]
+ 65952, // Range #224: [65952, 65952, Greek]
+ 66176, // Range #225: [66176, 66204, Lycian]
+ 66208, // Range #226: [66208, 66256, Carian]
+ 66304, // Range #227: [66304, 66339, Old_Italic]
+ 66349, // Range #228: [66349, 66351, Old_Italic]
+ 66352, // Range #229: [66352, 66378, Gothic]
+ 66384, // Range #230: [66384, 66426, Old_Permic]
+ 66432, // Range #231: [66432, 66463, Ugaritic]
+ 66464, // Range #232: [66464, 66517, Old_Persian]
+ 66560, // Range #233: [66560, 66639, Deseret]
+ 66640, // Range #234: [66640, 66687, Shavian]
+ 66688, // Range #235: [66688, 66729, Osmanya]
+ 66736, // Range #236: [66736, 66811, Osage]
+ 66816, // Range #237: [66816, 66855, Elbasan]
+ 66864, // Range #238: [66864, 66915, Caucasian_Albanian]
+ 66927, // Range #239: [66927, 66927, Caucasian_Albanian]
+ 66928, // Range #240: [66928, 67004, Vithkuqi]
+ 67072, // Range #241: [67072, 67382, Linear_A]
+ 67392, // Range #242: [67392, 67413, Linear_A]
+ 67424, // Range #243: [67424, 67431, Linear_A]
+ 67456, // Range #244: [67456, 67514, Latin]
+ 67584, // Range #245: [67584, 67647, Cypriot]
+ 67648, // Range #246: [67648, 67679, Imperial_Aramaic]
+ 67680, // Range #247: [67680, 67711, Palmyrene]
+ 67712, // Range #248: [67712, 67742, Nabataean]
+ 67751, // Range #249: [67751, 67759, Nabataean]
+ 67808, // Range #250: [67808, 67829, Hatran]
+ 67835, // Range #251: [67835, 67839, Hatran]
+ 67840, // Range #252: [67840, 67871, Phoenician]
+ 67872, // Range #253: [67872, 67897, Lydian]
+ 67903, // Range #254: [67903, 67903, Lydian]
+ 67968, // Range #255: [67968, 67999, Meroitic_Hieroglyphs]
+ 68000, // Range #256: [68000, 68095, Meroitic_Cursive]
+ 68096, // Range #257: [68096, 68102, Kharoshthi]
+ 68108, // Range #258: [68108, 68168, Kharoshthi]
+ 68176, // Range #259: [68176, 68184, Kharoshthi]
+ 68192, // Range #260: [68192, 68223, Old_South_Arabian]
+ 68224, // Range #261: [68224, 68255, Old_North_Arabian]
+ 68288, // Range #262: [68288, 68342, Manichaean]
+ 68352, // Range #263: [68352, 68415, Avestan]
+ 68416, // Range #264: [68416, 68447, Inscriptional_Parthian]
+ 68448, // Range #265: [68448, 68466, Inscriptional_Pahlavi]
+ 68472, // Range #266: [68472, 68479, Inscriptional_Pahlavi]
+ 68480, // Range #267: [68480, 68497, Psalter_Pahlavi]
+ 68505, // Range #268: [68505, 68508, Psalter_Pahlavi]
+ 68521, // Range #269: [68521, 68527, Psalter_Pahlavi]
+ 68608, // Range #270: [68608, 68680, Old_Turkic]
+ 68736, // Range #271: [68736, 68786, Old_Hungarian]
+ 68800, // Range #272: [68800, 68850, Old_Hungarian]
+ 68858, // Range #273: [68858, 68863, Old_Hungarian]
+ 68864, // Range #274: [68864, 68903, Hanifi_Rohingya]
+ 68912, // Range #275: [68912, 68921, Hanifi_Rohingya]
+ 69216, // Range #276: [69216, 69246, Arabic]
+ 69248, // Range #277: [69248, 69297, Yezidi]
+ 69376, // Range #278: [69376, 69415, Old_Sogdian]
+ 69424, // Range #279: [69424, 69465, Sogdian]
+ 69488, // Range #280: [69488, 69513, Old_Uyghur]
+ 69552, // Range #281: [69552, 69579, Chorasmian]
+ 69600, // Range #282: [69600, 69622, Elymaic]
+ 69632, // Range #283: [69632, 69749, Brahmi]
+ 69759, // Range #284: [69759, 69759, Brahmi]
+ 69760, // Range #285: [69760, 69826, Kaithi]
+ 69837, // Range #286: [69837, 69837, Kaithi]
+ 69840, // Range #287: [69840, 69864, Sora_Sompeng]
+ 69872, // Range #288: [69872, 69881, Sora_Sompeng]
+ 69888, // Range #289: [69888, 69959, Chakma]
+ 69968, // Range #290: [69968, 70006, Mahajani]
+ 70016, // Range #291: [70016, 70111, Sharada]
+ 70113, // Range #292: [70113, 70132, Sinhala]
+ 70144, // Range #293: [70144, 70206, Khojki]
+ 70272, // Range #294: [70272, 70313, Multani]
+ 70320, // Range #295: [70320, 70378, Khudawadi]
+ 70384, // Range #296: [70384, 70393, Khudawadi]
+ 70400, // Range #297: [70400, 70457, Grantha]
+ 70460, // Range #298: [70460, 70480, Grantha]
+ 70487, // Range #299: [70487, 70487, Grantha]
+ 70493, // Range #300: [70493, 70516, Grantha]
+ 70656, // Range #301: [70656, 70753, Newa]
+ 70784, // Range #302: [70784, 70855, Tirhuta]
+ 70864, // Range #303: [70864, 70873, Tirhuta]
+ 71040, // Range #304: [71040, 71133, Siddham]
+ 71168, // Range #305: [71168, 71236, Modi]
+ 71248, // Range #306: [71248, 71257, Modi]
+ 71264, // Range #307: [71264, 71276, Mongolian]
+ 71296, // Range #308: [71296, 71353, Takri]
+ 71360, // Range #309: [71360, 71369, Takri]
+ 71424, // Range #310: [71424, 71494, Ahom]
+ 71680, // Range #311: [71680, 71739, Dogra]
+ 71840, // Range #312: [71840, 71922, Warang_Citi]
+ 71935, // Range #313: [71935, 71935, Warang_Citi]
+ 71936, // Range #314: [71936, 72006, Dives_Akuru]
+ 72016, // Range #315: [72016, 72025, Dives_Akuru]
+ 72096, // Range #316: [72096, 72164, Nandinagari]
+ 72192, // Range #317: [72192, 72263, Zanabazar_Square]
+ 72272, // Range #318: [72272, 72354, Soyombo]
+ 72368, // Range #319: [72368, 72383, Canadian_Aboriginal]
+ 72384, // Range #320: [72384, 72440, Pau_Cin_Hau]
+ 72704, // Range #321: [72704, 72773, Bhaiksuki]
+ 72784, // Range #322: [72784, 72812, Bhaiksuki]
+ 72816, // Range #323: [72816, 72886, Marchen]
+ 72960, // Range #324: [72960, 73031, Masaram_Gondi]
+ 73040, // Range #325: [73040, 73049, Masaram_Gondi]
+ 73056, // Range #326: [73056, 73112, Gunjala_Gondi]
+ 73120, // Range #327: [73120, 73129, Gunjala_Gondi]
+ 73440, // Range #328: [73440, 73464, Makasar]
+ 73648, // Range #329: [73648, 73648, Lisu]
+ 73664, // Range #330: [73664, 73713, Tamil]
+ 73727, // Range #331: [73727, 73727, Tamil]
+ 73728, // Range #332: [73728, 74649, Cuneiform]
+ 74752, // Range #333: [74752, 74868, Cuneiform]
+ 74880, // Range #334: [74880, 75075, Cuneiform]
+ 77712, // Range #335: [77712, 77810, Cypro_Minoan]
+ 77824, // Range #336: [77824, 78904, Egyptian_Hieroglyphs]
+ 82944, // Range #337: [82944, 83526, Anatolian_Hieroglyphs]
+ 92160, // Range #338: [92160, 92728, Bamum]
+ 92736, // Range #339: [92736, 92783, Mro]
+ 92784, // Range #340: [92784, 92873, Tangsa]
+ 92880, // Range #341: [92880, 92917, Bassa_Vah]
+ 92928, // Range #342: [92928, 92997, Pahawh_Hmong]
+ 93008, // Range #343: [93008, 93047, Pahawh_Hmong]
+ 93053, // Range #344: [93053, 93071, Pahawh_Hmong]
+ 93760, // Range #345: [93760, 93850, Medefaidrin]
+ 93952, // Range #346: [93952, 94087, Miao]
+ 94095, // Range #347: [94095, 94111, Miao]
+ 94176, // Range #348: [94176, 94176, Tangut]
+ 94177, // Range #349: [94177, 94177, Nushu]
+ 94178, // Range #350: [94178, 94179, Han]
+ 94180, // Range #351: [94180, 94180, Khitan_Small_Script]
+ 94192, // Range #352: [94192, 94193, Han]
+ 94208, // Range #353: [94208, 100343, Tangut]
+ 100352, // Range #354: [100352, 101119, Tangut]
+ 101120, // Range #355: [101120, 101589, Khitan_Small_Script]
+ 101632, // Range #356: [101632, 101640, Tangut]
+ 110576, // Range #357: [110576, 110592, Katakana]
+ 110593, // Range #358: [110593, 110879, Hiragana]
+ 110880, // Range #359: [110880, 110882, Katakana]
+ 110928, // Range #360: [110928, 110930, Hiragana]
+ 110948, // Range #361: [110948, 110951, Katakana]
+ 110960, // Range #362: [110960, 111355, Nushu]
+ 113664, // Range #363: [113664, 113770, Duployan]
+ 113776, // Range #364: [113776, 113800, Duployan]
+ 113808, // Range #365: [113808, 113823, Duployan]
+ 119296, // Range #366: [119296, 119365, Greek]
+ 120832, // Range #367: [120832, 121483, SignWriting]
+ 121499, // Range #368: [121499, 121519, SignWriting]
+ 122624, // Range #369: [122624, 122654, Latin]
+ 122880, // Range #370: [122880, 122922, Glagolitic]
+ 123136, // Range #371: [123136, 123215, Nyiakeng_Puachue_Hmong]
+ 123536, // Range #372: [123536, 123566, Toto]
+ 123584, // Range #373: [123584, 123641, Wancho]
+ 123647, // Range #374: [123647, 123647, Wancho]
+ 124896, // Range #375: [124896, 124926, Ethiopic]
+ 124928, // Range #376: [124928, 125142, Mende_Kikakui]
+ 125184, // Range #377: [125184, 125279, Adlam]
+ 126464, // Range #378: [126464, 126523, Arabic]
+ 126530, // Range #379: [126530, 126619, Arabic]
+ 126625, // Range #380: [126625, 126651, Arabic]
+ 126704, // Range #381: [126704, 126705, Arabic]
+ 127488, // Range #382: [127488, 127488, Hiragana]
+ 131072, // Range #383: [131072, 173791, Han]
+ 173824, // Range #384: [173824, 177976, Han]
+ 177984, // Range #385: [177984, 183969, Han]
+ 183984, // Range #386: [183984, 191456, Han]
+ 194560, // Range #387: [194560, 195101, Han]
+ 196608, // Range #388: [196608, 201546, Han]
};
const uint16 kRangeSizeMinusOne[] = {
@@ -446,8 +459,8 @@
62, // Range #34: [2048, 2110, Samaritan]
30, // Range #35: [2112, 2142, Mandaic]
10, // Range #36: [2144, 2154, Syriac]
- 39, // Range #37: [2208, 2247, Arabic]
- 14, // Range #38: [2259, 2273, Arabic]
+ 33, // Range #37: [2160, 2193, Arabic]
+ 73, // Range #38: [2200, 2273, Arabic]
28, // Range #39: [2275, 2303, Arabic]
80, // Range #40: [2304, 2384, Devanagari]
14, // Range #41: [2389, 2403, Devanagari]
@@ -466,32 +479,32 @@
0, // Range #54: [3031, 3031, Tamil]
20, // Range #55: [3046, 3066, Tamil]
77, // Range #56: [3072, 3149, Telugu]
- 5, // Range #57: [3157, 3162, Telugu]
- 15, // Range #58: [3168, 3183, Telugu]
- 8, // Range #59: [3191, 3199, Telugu]
- 77, // Range #60: [3200, 3277, Kannada]
- 1, // Range #61: [3285, 3286, Kannada]
- 20, // Range #62: [3294, 3314, Kannada]
- 127, // Range #63: [3328, 3455, Malayalam]
- 94, // Range #64: [3457, 3551, Sinhala]
- 14, // Range #65: [3558, 3572, Sinhala]
- 57, // Range #66: [3585, 3642, Thai]
- 27, // Range #67: [3648, 3675, Thai]
- 94, // Range #68: [3713, 3807, Lao]
- 212, // Range #69: [3840, 4052, Tibetan]
- 1, // Range #70: [4057, 4058, Tibetan]
- 159, // Range #71: [4096, 4255, Myanmar]
- 39, // Range #72: [4256, 4295, Georgian]
- 45, // Range #73: [4301, 4346, Georgian]
- 3, // Range #74: [4348, 4351, Georgian]
- 255, // Range #75: [4352, 4607, Hangul]
- 409, // Range #76: [4608, 5017, Ethiopic]
- 93, // Range #77: [5024, 5117, Cherokee]
- 639, // Range #78: [5120, 5759, Canadian_Aboriginal]
- 28, // Range #79: [5760, 5788, Ogham]
- 74, // Range #80: [5792, 5866, Runic]
- 10, // Range #81: [5870, 5880, Runic]
- 20, // Range #82: [5888, 5908, Tagalog]
+ 26, // Range #57: [3157, 3183, Telugu]
+ 8, // Range #58: [3191, 3199, Telugu]
+ 77, // Range #59: [3200, 3277, Kannada]
+ 1, // Range #60: [3285, 3286, Kannada]
+ 21, // Range #61: [3293, 3314, Kannada]
+ 127, // Range #62: [3328, 3455, Malayalam]
+ 94, // Range #63: [3457, 3551, Sinhala]
+ 14, // Range #64: [3558, 3572, Sinhala]
+ 57, // Range #65: [3585, 3642, Thai]
+ 27, // Range #66: [3648, 3675, Thai]
+ 94, // Range #67: [3713, 3807, Lao]
+ 212, // Range #68: [3840, 4052, Tibetan]
+ 1, // Range #69: [4057, 4058, Tibetan]
+ 159, // Range #70: [4096, 4255, Myanmar]
+ 39, // Range #71: [4256, 4295, Georgian]
+ 45, // Range #72: [4301, 4346, Georgian]
+ 3, // Range #73: [4348, 4351, Georgian]
+ 255, // Range #74: [4352, 4607, Hangul]
+ 409, // Range #75: [4608, 5017, Ethiopic]
+ 93, // Range #76: [5024, 5117, Cherokee]
+ 639, // Range #77: [5120, 5759, Canadian_Aboriginal]
+ 28, // Range #78: [5760, 5788, Ogham]
+ 74, // Range #79: [5792, 5866, Runic]
+ 10, // Range #80: [5870, 5880, Runic]
+ 21, // Range #81: [5888, 5909, Tagalog]
+ 0, // Range #82: [5919, 5919, Tagalog]
20, // Range #83: [5920, 5940, Hanunoo]
19, // Range #84: [5952, 5971, Buhid]
19, // Range #85: [5984, 6003, Tagbanwa]
@@ -512,7 +525,7 @@
105, // Range #100: [6688, 6793, Tai_Tham]
9, // Range #101: [6800, 6809, Tai_Tham]
13, // Range #102: [6816, 6829, Tai_Tham]
- 124, // Range #103: [6912, 7036, Balinese]
+ 126, // Range #103: [6912, 7038, Balinese]
63, // Range #104: [7040, 7103, Sundanese]
51, // Range #105: [7104, 7155, Batak]
3, // Range #106: [7164, 7167, Batak]
@@ -543,7 +556,7 @@
0, // Range #131: [8526, 8526, Latin]
40, // Range #132: [8544, 8584, Latin]
255, // Range #133: [10240, 10495, Braille]
- 94, // Range #134: [11264, 11358, Glagolitic]
+ 95, // Range #134: [11264, 11359, Glagolitic]
31, // Range #135: [11360, 11391, Latin]
115, // Range #136: [11392, 11507, Coptic]
6, // Range #137: [11513, 11519, Coptic]
@@ -575,7 +588,7 @@
46, // Range #163: [13008, 13054, Katakana]
87, // Range #164: [13056, 13143, Katakana]
6591, // Range #165: [13312, 19903, Han]
- 20988, // Range #166: [19968, 40956, Han]
+ 20991, // Range #166: [19968, 40959, Han]
1222, // Range #167: [40960, 42182, Yi]
47, // Range #168: [42192, 42239, Lisu]
299, // Range #169: [42240, 42539, Vai]
@@ -583,208 +596,221 @@
87, // Range #171: [42656, 42743, Bamum]
101, // Range #172: [42786, 42887, Latin]
63, // Range #173: [42891, 42954, Latin]
- 10, // Range #174: [42997, 43007, Latin]
- 44, // Range #175: [43008, 43052, Syloti_Nagri]
- 55, // Range #176: [43072, 43127, Phags_Pa]
- 69, // Range #177: [43136, 43205, Saurashtra]
- 11, // Range #178: [43214, 43225, Saurashtra]
- 31, // Range #179: [43232, 43263, Devanagari]
- 45, // Range #180: [43264, 43309, Kayah_Li]
- 0, // Range #181: [43311, 43311, Kayah_Li]
- 35, // Range #182: [43312, 43347, Rejang]
- 0, // Range #183: [43359, 43359, Rejang]
- 28, // Range #184: [43360, 43388, Hangul]
- 77, // Range #185: [43392, 43469, Javanese]
- 15, // Range #186: [43472, 43487, Javanese]
- 30, // Range #187: [43488, 43518, Myanmar]
- 54, // Range #188: [43520, 43574, Cham]
- 31, // Range #189: [43584, 43615, Cham]
- 31, // Range #190: [43616, 43647, Myanmar]
- 66, // Range #191: [43648, 43714, Tai_Viet]
- 4, // Range #192: [43739, 43743, Tai_Viet]
- 22, // Range #193: [43744, 43766, Meetei_Mayek]
- 21, // Range #194: [43777, 43798, Ethiopic]
- 14, // Range #195: [43808, 43822, Ethiopic]
- 42, // Range #196: [43824, 43866, Latin]
- 8, // Range #197: [43868, 43876, Latin]
- 0, // Range #198: [43877, 43877, Greek]
- 3, // Range #199: [43878, 43881, Latin]
- 79, // Range #200: [43888, 43967, Cherokee]
- 57, // Range #201: [43968, 44025, Meetei_Mayek]
- 11171, // Range #202: [44032, 55203, Hangul]
- 75, // Range #203: [55216, 55291, Hangul]
- 473, // Range #204: [63744, 64217, Han]
- 6, // Range #205: [64256, 64262, Latin]
- 4, // Range #206: [64275, 64279, Armenian]
- 50, // Range #207: [64285, 64335, Hebrew]
- 113, // Range #208: [64336, 64449, Arabic]
- 362, // Range #209: [64467, 64829, Arabic]
- 119, // Range #210: [64848, 64967, Arabic]
- 13, // Range #211: [65008, 65021, Arabic]
- 1, // Range #212: [65070, 65071, Cyrillic]
- 140, // Range #213: [65136, 65276, Arabic]
- 25, // Range #214: [65313, 65338, Latin]
- 25, // Range #215: [65345, 65370, Latin]
- 9, // Range #216: [65382, 65391, Katakana]
- 44, // Range #217: [65393, 65437, Katakana]
- 60, // Range #218: [65440, 65500, Hangul]
- 93, // Range #219: [65536, 65629, Linear_B]
- 122, // Range #220: [65664, 65786, Linear_B]
- 78, // Range #221: [65856, 65934, Greek]
- 0, // Range #222: [65952, 65952, Greek]
- 28, // Range #223: [66176, 66204, Lycian]
- 48, // Range #224: [66208, 66256, Carian]
- 35, // Range #225: [66304, 66339, Old_Italic]
- 2, // Range #226: [66349, 66351, Old_Italic]
- 26, // Range #227: [66352, 66378, Gothic]
- 42, // Range #228: [66384, 66426, Old_Permic]
- 31, // Range #229: [66432, 66463, Ugaritic]
- 53, // Range #230: [66464, 66517, Old_Persian]
- 79, // Range #231: [66560, 66639, Deseret]
- 47, // Range #232: [66640, 66687, Shavian]
- 41, // Range #233: [66688, 66729, Osmanya]
- 75, // Range #234: [66736, 66811, Osage]
- 39, // Range #235: [66816, 66855, Elbasan]
- 51, // Range #236: [66864, 66915, Caucasian_Albanian]
- 0, // Range #237: [66927, 66927, Caucasian_Albanian]
- 310, // Range #238: [67072, 67382, Linear_A]
- 21, // Range #239: [67392, 67413, Linear_A]
- 7, // Range #240: [67424, 67431, Linear_A]
- 63, // Range #241: [67584, 67647, Cypriot]
- 31, // Range #242: [67648, 67679, Imperial_Aramaic]
- 31, // Range #243: [67680, 67711, Palmyrene]
- 30, // Range #244: [67712, 67742, Nabataean]
- 8, // Range #245: [67751, 67759, Nabataean]
- 21, // Range #246: [67808, 67829, Hatran]
- 4, // Range #247: [67835, 67839, Hatran]
- 31, // Range #248: [67840, 67871, Phoenician]
- 25, // Range #249: [67872, 67897, Lydian]
- 0, // Range #250: [67903, 67903, Lydian]
- 31, // Range #251: [67968, 67999, Meroitic_Hieroglyphs]
- 95, // Range #252: [68000, 68095, Meroitic_Cursive]
- 6, // Range #253: [68096, 68102, Kharoshthi]
- 60, // Range #254: [68108, 68168, Kharoshthi]
- 8, // Range #255: [68176, 68184, Kharoshthi]
- 31, // Range #256: [68192, 68223, Old_South_Arabian]
- 31, // Range #257: [68224, 68255, Old_North_Arabian]
- 54, // Range #258: [68288, 68342, Manichaean]
- 63, // Range #259: [68352, 68415, Avestan]
- 31, // Range #260: [68416, 68447, Inscriptional_Parthian]
- 18, // Range #261: [68448, 68466, Inscriptional_Pahlavi]
- 7, // Range #262: [68472, 68479, Inscriptional_Pahlavi]
- 17, // Range #263: [68480, 68497, Psalter_Pahlavi]
- 3, // Range #264: [68505, 68508, Psalter_Pahlavi]
- 6, // Range #265: [68521, 68527, Psalter_Pahlavi]
- 72, // Range #266: [68608, 68680, Old_Turkic]
- 50, // Range #267: [68736, 68786, Old_Hungarian]
- 50, // Range #268: [68800, 68850, Old_Hungarian]
- 5, // Range #269: [68858, 68863, Old_Hungarian]
- 39, // Range #270: [68864, 68903, Hanifi_Rohingya]
- 9, // Range #271: [68912, 68921, Hanifi_Rohingya]
- 30, // Range #272: [69216, 69246, Arabic]
- 49, // Range #273: [69248, 69297, Yezidi]
- 39, // Range #274: [69376, 69415, Old_Sogdian]
- 41, // Range #275: [69424, 69465, Sogdian]
- 27, // Range #276: [69552, 69579, Chorasmian]
- 22, // Range #277: [69600, 69622, Elymaic]
- 111, // Range #278: [69632, 69743, Brahmi]
- 0, // Range #279: [69759, 69759, Brahmi]
- 65, // Range #280: [69760, 69825, Kaithi]
- 0, // Range #281: [69837, 69837, Kaithi]
- 24, // Range #282: [69840, 69864, Sora_Sompeng]
- 9, // Range #283: [69872, 69881, Sora_Sompeng]
- 71, // Range #284: [69888, 69959, Chakma]
- 38, // Range #285: [69968, 70006, Mahajani]
- 95, // Range #286: [70016, 70111, Sharada]
- 19, // Range #287: [70113, 70132, Sinhala]
- 62, // Range #288: [70144, 70206, Khojki]
- 41, // Range #289: [70272, 70313, Multani]
- 58, // Range #290: [70320, 70378, Khudawadi]
- 9, // Range #291: [70384, 70393, Khudawadi]
- 57, // Range #292: [70400, 70457, Grantha]
- 20, // Range #293: [70460, 70480, Grantha]
- 0, // Range #294: [70487, 70487, Grantha]
- 23, // Range #295: [70493, 70516, Grantha]
- 97, // Range #296: [70656, 70753, Newa]
- 71, // Range #297: [70784, 70855, Tirhuta]
- 9, // Range #298: [70864, 70873, Tirhuta]
- 93, // Range #299: [71040, 71133, Siddham]
- 68, // Range #300: [71168, 71236, Modi]
- 9, // Range #301: [71248, 71257, Modi]
- 12, // Range #302: [71264, 71276, Mongolian]
- 56, // Range #303: [71296, 71352, Takri]
- 9, // Range #304: [71360, 71369, Takri]
- 63, // Range #305: [71424, 71487, Ahom]
- 59, // Range #306: [71680, 71739, Dogra]
- 82, // Range #307: [71840, 71922, Warang_Citi]
- 0, // Range #308: [71935, 71935, Warang_Citi]
- 70, // Range #309: [71936, 72006, Dives_Akuru]
- 9, // Range #310: [72016, 72025, Dives_Akuru]
- 68, // Range #311: [72096, 72164, Nandinagari]
- 71, // Range #312: [72192, 72263, Zanabazar_Square]
- 82, // Range #313: [72272, 72354, Soyombo]
- 56, // Range #314: [72384, 72440, Pau_Cin_Hau]
- 69, // Range #315: [72704, 72773, Bhaiksuki]
- 28, // Range #316: [72784, 72812, Bhaiksuki]
- 70, // Range #317: [72816, 72886, Marchen]
- 71, // Range #318: [72960, 73031, Masaram_Gondi]
- 9, // Range #319: [73040, 73049, Masaram_Gondi]
- 56, // Range #320: [73056, 73112, Gunjala_Gondi]
- 9, // Range #321: [73120, 73129, Gunjala_Gondi]
- 24, // Range #322: [73440, 73464, Makasar]
- 0, // Range #323: [73648, 73648, Lisu]
- 49, // Range #324: [73664, 73713, Tamil]
- 0, // Range #325: [73727, 73727, Tamil]
- 921, // Range #326: [73728, 74649, Cuneiform]
- 116, // Range #327: [74752, 74868, Cuneiform]
- 195, // Range #328: [74880, 75075, Cuneiform]
- 1080, // Range #329: [77824, 78904, Egyptian_Hieroglyphs]
- 582, // Range #330: [82944, 83526, Anatolian_Hieroglyphs]
- 568, // Range #331: [92160, 92728, Bamum]
- 47, // Range #332: [92736, 92783, Mro]
- 37, // Range #333: [92880, 92917, Bassa_Vah]
- 69, // Range #334: [92928, 92997, Pahawh_Hmong]
- 39, // Range #335: [93008, 93047, Pahawh_Hmong]
- 18, // Range #336: [93053, 93071, Pahawh_Hmong]
- 90, // Range #337: [93760, 93850, Medefaidrin]
- 135, // Range #338: [93952, 94087, Miao]
- 16, // Range #339: [94095, 94111, Miao]
- 0, // Range #340: [94176, 94176, Tangut]
- 0, // Range #341: [94177, 94177, Nushu]
- 0, // Range #342: [94180, 94180, Khitan_Small_Script]
- 1, // Range #343: [94192, 94193, Han]
- 6135, // Range #344: [94208, 100343, Tangut]
- 767, // Range #345: [100352, 101119, Tangut]
- 469, // Range #346: [101120, 101589, Khitan_Small_Script]
- 8, // Range #347: [101632, 101640, Tangut]
- 0, // Range #348: [110592, 110592, Katakana]
- 285, // Range #349: [110593, 110878, Hiragana]
- 2, // Range #350: [110928, 110930, Hiragana]
- 3, // Range #351: [110948, 110951, Katakana]
- 395, // Range #352: [110960, 111355, Nushu]
- 106, // Range #353: [113664, 113770, Duployan]
- 24, // Range #354: [113776, 113800, Duployan]
- 15, // Range #355: [113808, 113823, Duployan]
- 69, // Range #356: [119296, 119365, Greek]
- 651, // Range #357: [120832, 121483, SignWriting]
- 20, // Range #358: [121499, 121519, SignWriting]
- 42, // Range #359: [122880, 122922, Glagolitic]
- 79, // Range #360: [123136, 123215, Nyiakeng_Puachue_Hmong]
- 57, // Range #361: [123584, 123641, Wancho]
- 0, // Range #362: [123647, 123647, Wancho]
- 214, // Range #363: [124928, 125142, Mende_Kikakui]
- 95, // Range #364: [125184, 125279, Adlam]
- 59, // Range #365: [126464, 126523, Arabic]
- 89, // Range #366: [126530, 126619, Arabic]
- 26, // Range #367: [126625, 126651, Arabic]
- 1, // Range #368: [126704, 126705, Arabic]
- 0, // Range #369: [127488, 127488, Hiragana]
- 42717, // Range #370: [131072, 173789, Han]
- 4148, // Range #371: [173824, 177972, Han]
- 5985, // Range #372: [177984, 183969, Han]
- 7472, // Range #373: [183984, 191456, Han]
- 541, // Range #374: [194560, 195101, Han]
- 4938, // Range #375: [196608, 201546, Han]
+ 9, // Range #174: [42960, 42969, Latin]
+ 13, // Range #175: [42994, 43007, Latin]
+ 44, // Range #176: [43008, 43052, Syloti_Nagri]
+ 55, // Range #177: [43072, 43127, Phags_Pa]
+ 69, // Range #178: [43136, 43205, Saurashtra]
+ 11, // Range #179: [43214, 43225, Saurashtra]
+ 31, // Range #180: [43232, 43263, Devanagari]
+ 45, // Range #181: [43264, 43309, Kayah_Li]
+ 0, // Range #182: [43311, 43311, Kayah_Li]
+ 35, // Range #183: [43312, 43347, Rejang]
+ 0, // Range #184: [43359, 43359, Rejang]
+ 28, // Range #185: [43360, 43388, Hangul]
+ 77, // Range #186: [43392, 43469, Javanese]
+ 15, // Range #187: [43472, 43487, Javanese]
+ 30, // Range #188: [43488, 43518, Myanmar]
+ 54, // Range #189: [43520, 43574, Cham]
+ 31, // Range #190: [43584, 43615, Cham]
+ 31, // Range #191: [43616, 43647, Myanmar]
+ 66, // Range #192: [43648, 43714, Tai_Viet]
+ 4, // Range #193: [43739, 43743, Tai_Viet]
+ 22, // Range #194: [43744, 43766, Meetei_Mayek]
+ 21, // Range #195: [43777, 43798, Ethiopic]
+ 14, // Range #196: [43808, 43822, Ethiopic]
+ 42, // Range #197: [43824, 43866, Latin]
+ 8, // Range #198: [43868, 43876, Latin]
+ 0, // Range #199: [43877, 43877, Greek]
+ 3, // Range #200: [43878, 43881, Latin]
+ 79, // Range #201: [43888, 43967, Cherokee]
+ 57, // Range #202: [43968, 44025, Meetei_Mayek]
+ 11171, // Range #203: [44032, 55203, Hangul]
+ 75, // Range #204: [55216, 55291, Hangul]
+ 473, // Range #205: [63744, 64217, Han]
+ 6, // Range #206: [64256, 64262, Latin]
+ 4, // Range #207: [64275, 64279, Armenian]
+ 50, // Range #208: [64285, 64335, Hebrew]
+ 114, // Range #209: [64336, 64450, Arabic]
+ 362, // Range #210: [64467, 64829, Arabic]
+ 135, // Range #211: [64832, 64967, Arabic]
+ 0, // Range #212: [64975, 64975, Arabic]
+ 15, // Range #213: [65008, 65023, Arabic]
+ 1, // Range #214: [65070, 65071, Cyrillic]
+ 140, // Range #215: [65136, 65276, Arabic]
+ 25, // Range #216: [65313, 65338, Latin]
+ 25, // Range #217: [65345, 65370, Latin]
+ 9, // Range #218: [65382, 65391, Katakana]
+ 44, // Range #219: [65393, 65437, Katakana]
+ 60, // Range #220: [65440, 65500, Hangul]
+ 93, // Range #221: [65536, 65629, Linear_B]
+ 122, // Range #222: [65664, 65786, Linear_B]
+ 78, // Range #223: [65856, 65934, Greek]
+ 0, // Range #224: [65952, 65952, Greek]
+ 28, // Range #225: [66176, 66204, Lycian]
+ 48, // Range #226: [66208, 66256, Carian]
+ 35, // Range #227: [66304, 66339, Old_Italic]
+ 2, // Range #228: [66349, 66351, Old_Italic]
+ 26, // Range #229: [66352, 66378, Gothic]
+ 42, // Range #230: [66384, 66426, Old_Permic]
+ 31, // Range #231: [66432, 66463, Ugaritic]
+ 53, // Range #232: [66464, 66517, Old_Persian]
+ 79, // Range #233: [66560, 66639, Deseret]
+ 47, // Range #234: [66640, 66687, Shavian]
+ 41, // Range #235: [66688, 66729, Osmanya]
+ 75, // Range #236: [66736, 66811, Osage]
+ 39, // Range #237: [66816, 66855, Elbasan]
+ 51, // Range #238: [66864, 66915, Caucasian_Albanian]
+ 0, // Range #239: [66927, 66927, Caucasian_Albanian]
+ 76, // Range #240: [66928, 67004, Vithkuqi]
+ 310, // Range #241: [67072, 67382, Linear_A]
+ 21, // Range #242: [67392, 67413, Linear_A]
+ 7, // Range #243: [67424, 67431, Linear_A]
+ 58, // Range #244: [67456, 67514, Latin]
+ 63, // Range #245: [67584, 67647, Cypriot]
+ 31, // Range #246: [67648, 67679, Imperial_Aramaic]
+ 31, // Range #247: [67680, 67711, Palmyrene]
+ 30, // Range #248: [67712, 67742, Nabataean]
+ 8, // Range #249: [67751, 67759, Nabataean]
+ 21, // Range #250: [67808, 67829, Hatran]
+ 4, // Range #251: [67835, 67839, Hatran]
+ 31, // Range #252: [67840, 67871, Phoenician]
+ 25, // Range #253: [67872, 67897, Lydian]
+ 0, // Range #254: [67903, 67903, Lydian]
+ 31, // Range #255: [67968, 67999, Meroitic_Hieroglyphs]
+ 95, // Range #256: [68000, 68095, Meroitic_Cursive]
+ 6, // Range #257: [68096, 68102, Kharoshthi]
+ 60, // Range #258: [68108, 68168, Kharoshthi]
+ 8, // Range #259: [68176, 68184, Kharoshthi]
+ 31, // Range #260: [68192, 68223, Old_South_Arabian]
+ 31, // Range #261: [68224, 68255, Old_North_Arabian]
+ 54, // Range #262: [68288, 68342, Manichaean]
+ 63, // Range #263: [68352, 68415, Avestan]
+ 31, // Range #264: [68416, 68447, Inscriptional_Parthian]
+ 18, // Range #265: [68448, 68466, Inscriptional_Pahlavi]
+ 7, // Range #266: [68472, 68479, Inscriptional_Pahlavi]
+ 17, // Range #267: [68480, 68497, Psalter_Pahlavi]
+ 3, // Range #268: [68505, 68508, Psalter_Pahlavi]
+ 6, // Range #269: [68521, 68527, Psalter_Pahlavi]
+ 72, // Range #270: [68608, 68680, Old_Turkic]
+ 50, // Range #271: [68736, 68786, Old_Hungarian]
+ 50, // Range #272: [68800, 68850, Old_Hungarian]
+ 5, // Range #273: [68858, 68863, Old_Hungarian]
+ 39, // Range #274: [68864, 68903, Hanifi_Rohingya]
+ 9, // Range #275: [68912, 68921, Hanifi_Rohingya]
+ 30, // Range #276: [69216, 69246, Arabic]
+ 49, // Range #277: [69248, 69297, Yezidi]
+ 39, // Range #278: [69376, 69415, Old_Sogdian]
+ 41, // Range #279: [69424, 69465, Sogdian]
+ 25, // Range #280: [69488, 69513, Old_Uyghur]
+ 27, // Range #281: [69552, 69579, Chorasmian]
+ 22, // Range #282: [69600, 69622, Elymaic]
+ 117, // Range #283: [69632, 69749, Brahmi]
+ 0, // Range #284: [69759, 69759, Brahmi]
+ 66, // Range #285: [69760, 69826, Kaithi]
+ 0, // Range #286: [69837, 69837, Kaithi]
+ 24, // Range #287: [69840, 69864, Sora_Sompeng]
+ 9, // Range #288: [69872, 69881, Sora_Sompeng]
+ 71, // Range #289: [69888, 69959, Chakma]
+ 38, // Range #290: [69968, 70006, Mahajani]
+ 95, // Range #291: [70016, 70111, Sharada]
+ 19, // Range #292: [70113, 70132, Sinhala]
+ 62, // Range #293: [70144, 70206, Khojki]
+ 41, // Range #294: [70272, 70313, Multani]
+ 58, // Range #295: [70320, 70378, Khudawadi]
+ 9, // Range #296: [70384, 70393, Khudawadi]
+ 57, // Range #297: [70400, 70457, Grantha]
+ 20, // Range #298: [70460, 70480, Grantha]
+ 0, // Range #299: [70487, 70487, Grantha]
+ 23, // Range #300: [70493, 70516, Grantha]
+ 97, // Range #301: [70656, 70753, Newa]
+ 71, // Range #302: [70784, 70855, Tirhuta]
+ 9, // Range #303: [70864, 70873, Tirhuta]
+ 93, // Range #304: [71040, 71133, Siddham]
+ 68, // Range #305: [71168, 71236, Modi]
+ 9, // Range #306: [71248, 71257, Modi]
+ 12, // Range #307: [71264, 71276, Mongolian]
+ 57, // Range #308: [71296, 71353, Takri]
+ 9, // Range #309: [71360, 71369, Takri]
+ 70, // Range #310: [71424, 71494, Ahom]
+ 59, // Range #311: [71680, 71739, Dogra]
+ 82, // Range #312: [71840, 71922, Warang_Citi]
+ 0, // Range #313: [71935, 71935, Warang_Citi]
+ 70, // Range #314: [71936, 72006, Dives_Akuru]
+ 9, // Range #315: [72016, 72025, Dives_Akuru]
+ 68, // Range #316: [72096, 72164, Nandinagari]
+ 71, // Range #317: [72192, 72263, Zanabazar_Square]
+ 82, // Range #318: [72272, 72354, Soyombo]
+ 15, // Range #319: [72368, 72383, Canadian_Aboriginal]
+ 56, // Range #320: [72384, 72440, Pau_Cin_Hau]
+ 69, // Range #321: [72704, 72773, Bhaiksuki]
+ 28, // Range #322: [72784, 72812, Bhaiksuki]
+ 70, // Range #323: [72816, 72886, Marchen]
+ 71, // Range #324: [72960, 73031, Masaram_Gondi]
+ 9, // Range #325: [73040, 73049, Masaram_Gondi]
+ 56, // Range #326: [73056, 73112, Gunjala_Gondi]
+ 9, // Range #327: [73120, 73129, Gunjala_Gondi]
+ 24, // Range #328: [73440, 73464, Makasar]
+ 0, // Range #329: [73648, 73648, Lisu]
+ 49, // Range #330: [73664, 73713, Tamil]
+ 0, // Range #331: [73727, 73727, Tamil]
+ 921, // Range #332: [73728, 74649, Cuneiform]
+ 116, // Range #333: [74752, 74868, Cuneiform]
+ 195, // Range #334: [74880, 75075, Cuneiform]
+ 98, // Range #335: [77712, 77810, Cypro_Minoan]
+ 1080, // Range #336: [77824, 78904, Egyptian_Hieroglyphs]
+ 582, // Range #337: [82944, 83526, Anatolian_Hieroglyphs]
+ 568, // Range #338: [92160, 92728, Bamum]
+ 47, // Range #339: [92736, 92783, Mro]
+ 89, // Range #340: [92784, 92873, Tangsa]
+ 37, // Range #341: [92880, 92917, Bassa_Vah]
+ 69, // Range #342: [92928, 92997, Pahawh_Hmong]
+ 39, // Range #343: [93008, 93047, Pahawh_Hmong]
+ 18, // Range #344: [93053, 93071, Pahawh_Hmong]
+ 90, // Range #345: [93760, 93850, Medefaidrin]
+ 135, // Range #346: [93952, 94087, Miao]
+ 16, // Range #347: [94095, 94111, Miao]
+ 0, // Range #348: [94176, 94176, Tangut]
+ 0, // Range #349: [94177, 94177, Nushu]
+ 1, // Range #350: [94178, 94179, Han]
+ 0, // Range #351: [94180, 94180, Khitan_Small_Script]
+ 1, // Range #352: [94192, 94193, Han]
+ 6135, // Range #353: [94208, 100343, Tangut]
+ 767, // Range #354: [100352, 101119, Tangut]
+ 469, // Range #355: [101120, 101589, Khitan_Small_Script]
+ 8, // Range #356: [101632, 101640, Tangut]
+ 16, // Range #357: [110576, 110592, Katakana]
+ 286, // Range #358: [110593, 110879, Hiragana]
+ 2, // Range #359: [110880, 110882, Katakana]
+ 2, // Range #360: [110928, 110930, Hiragana]
+ 3, // Range #361: [110948, 110951, Katakana]
+ 395, // Range #362: [110960, 111355, Nushu]
+ 106, // Range #363: [113664, 113770, Duployan]
+ 24, // Range #364: [113776, 113800, Duployan]
+ 15, // Range #365: [113808, 113823, Duployan]
+ 69, // Range #366: [119296, 119365, Greek]
+ 651, // Range #367: [120832, 121483, SignWriting]
+ 20, // Range #368: [121499, 121519, SignWriting]
+ 30, // Range #369: [122624, 122654, Latin]
+ 42, // Range #370: [122880, 122922, Glagolitic]
+ 79, // Range #371: [123136, 123215, Nyiakeng_Puachue_Hmong]
+ 30, // Range #372: [123536, 123566, Toto]
+ 57, // Range #373: [123584, 123641, Wancho]
+ 0, // Range #374: [123647, 123647, Wancho]
+ 30, // Range #375: [124896, 124926, Ethiopic]
+ 214, // Range #376: [124928, 125142, Mende_Kikakui]
+ 95, // Range #377: [125184, 125279, Adlam]
+ 59, // Range #378: [126464, 126523, Arabic]
+ 89, // Range #379: [126530, 126619, Arabic]
+ 26, // Range #380: [126625, 126651, Arabic]
+ 1, // Range #381: [126704, 126705, Arabic]
+ 0, // Range #382: [127488, 127488, Hiragana]
+ 42719, // Range #383: [131072, 173791, Han]
+ 4152, // Range #384: [173824, 177976, Han]
+ 5985, // Range #385: [177984, 183969, Han]
+ 7472, // Range #386: [183984, 191456, Han]
+ 541, // Range #387: [194560, 195101, Han]
+ 4938, // Range #388: [196608, 201546, Han]
};
const uint8 kRangeScript[] = {
@@ -825,8 +851,8 @@
126, // Range #34: [2048, 2110, Samaritan]
84, // Range #35: [2112, 2142, Mandaic]
34, // Range #36: [2144, 2154, Syriac]
- 2, // Range #37: [2208, 2247, Arabic]
- 2, // Range #38: [2259, 2273, Arabic]
+ 2, // Range #37: [2160, 2193, Arabic]
+ 2, // Range #38: [2200, 2273, Arabic]
2, // Range #39: [2275, 2303, Arabic]
10, // Range #40: [2304, 2384, Devanagari]
10, // Range #41: [2389, 2403, Devanagari]
@@ -845,32 +871,32 @@
35, // Range #54: [3031, 3031, Tamil]
35, // Range #55: [3046, 3066, Tamil]
36, // Range #56: [3072, 3149, Telugu]
- 36, // Range #57: [3157, 3162, Telugu]
- 36, // Range #58: [3168, 3183, Telugu]
- 36, // Range #59: [3191, 3199, Telugu]
- 21, // Range #60: [3200, 3277, Kannada]
- 21, // Range #61: [3285, 3286, Kannada]
- 21, // Range #62: [3294, 3314, Kannada]
- 26, // Range #63: [3328, 3455, Malayalam]
- 33, // Range #64: [3457, 3551, Sinhala]
- 33, // Range #65: [3558, 3572, Sinhala]
- 38, // Range #66: [3585, 3642, Thai]
- 38, // Range #67: [3648, 3675, Thai]
- 24, // Range #68: [3713, 3807, Lao]
- 39, // Range #69: [3840, 4052, Tibetan]
- 39, // Range #70: [4057, 4058, Tibetan]
- 28, // Range #71: [4096, 4255, Myanmar]
- 12, // Range #72: [4256, 4295, Georgian]
- 12, // Range #73: [4301, 4346, Georgian]
- 12, // Range #74: [4348, 4351, Georgian]
- 18, // Range #75: [4352, 4607, Hangul]
- 11, // Range #76: [4608, 5017, Ethiopic]
- 6, // Range #77: [5024, 5117, Cherokee]
- 40, // Range #78: [5120, 5759, Canadian_Aboriginal]
- 29, // Range #79: [5760, 5788, Ogham]
- 32, // Range #80: [5792, 5866, Runic]
- 32, // Range #81: [5870, 5880, Runic]
- 42, // Range #82: [5888, 5908, Tagalog]
+ 36, // Range #57: [3157, 3183, Telugu]
+ 36, // Range #58: [3191, 3199, Telugu]
+ 21, // Range #59: [3200, 3277, Kannada]
+ 21, // Range #60: [3285, 3286, Kannada]
+ 21, // Range #61: [3293, 3314, Kannada]
+ 26, // Range #62: [3328, 3455, Malayalam]
+ 33, // Range #63: [3457, 3551, Sinhala]
+ 33, // Range #64: [3558, 3572, Sinhala]
+ 38, // Range #65: [3585, 3642, Thai]
+ 38, // Range #66: [3648, 3675, Thai]
+ 24, // Range #67: [3713, 3807, Lao]
+ 39, // Range #68: [3840, 4052, Tibetan]
+ 39, // Range #69: [4057, 4058, Tibetan]
+ 28, // Range #70: [4096, 4255, Myanmar]
+ 12, // Range #71: [4256, 4295, Georgian]
+ 12, // Range #72: [4301, 4346, Georgian]
+ 12, // Range #73: [4348, 4351, Georgian]
+ 18, // Range #74: [4352, 4607, Hangul]
+ 11, // Range #75: [4608, 5017, Ethiopic]
+ 6, // Range #76: [5024, 5117, Cherokee]
+ 40, // Range #77: [5120, 5759, Canadian_Aboriginal]
+ 29, // Range #78: [5760, 5788, Ogham]
+ 32, // Range #79: [5792, 5866, Runic]
+ 32, // Range #80: [5870, 5880, Runic]
+ 42, // Range #81: [5888, 5909, Tagalog]
+ 42, // Range #82: [5919, 5919, Tagalog]
43, // Range #83: [5920, 5940, Hanunoo]
44, // Range #84: [5952, 5971, Buhid]
45, // Range #85: [5984, 6003, Tagbanwa]
@@ -891,7 +917,7 @@
106, // Range #100: [6688, 6793, Tai_Tham]
106, // Range #101: [6800, 6809, Tai_Tham]
106, // Range #102: [6816, 6829, Tai_Tham]
- 62, // Range #103: [6912, 7036, Balinese]
+ 62, // Range #103: [6912, 7038, Balinese]
113, // Range #104: [7040, 7103, Sundanese]
63, // Range #105: [7104, 7155, Batak]
63, // Range #106: [7164, 7167, Batak]
@@ -922,7 +948,7 @@
25, // Range #131: [8526, 8526, Latin]
25, // Range #132: [8544, 8584, Latin]
46, // Range #133: [10240, 10495, Braille]
- 56, // Range #134: [11264, 11358, Glagolitic]
+ 56, // Range #134: [11264, 11359, Glagolitic]
25, // Range #135: [11360, 11391, Latin]
7, // Range #136: [11392, 11507, Coptic]
7, // Range #137: [11513, 11519, Coptic]
@@ -954,7 +980,7 @@
22, // Range #163: [13008, 13054, Katakana]
22, // Range #164: [13056, 13143, Katakana]
17, // Range #165: [13312, 19903, Han]
- 17, // Range #166: [19968, 40956, Han]
+ 17, // Range #166: [19968, 40959, Han]
41, // Range #167: [40960, 42182, Yi]
131, // Range #168: [42192, 42239, Lisu]
99, // Range #169: [42240, 42539, Vai]
@@ -962,211 +988,224 @@
130, // Range #171: [42656, 42743, Bamum]
25, // Range #172: [42786, 42887, Latin]
25, // Range #173: [42891, 42954, Latin]
- 25, // Range #174: [42997, 43007, Latin]
- 58, // Range #175: [43008, 43052, Syloti_Nagri]
- 90, // Range #176: [43072, 43127, Phags_Pa]
- 111, // Range #177: [43136, 43205, Saurashtra]
- 111, // Range #178: [43214, 43225, Saurashtra]
- 10, // Range #179: [43232, 43263, Devanagari]
- 79, // Range #180: [43264, 43309, Kayah_Li]
- 79, // Range #181: [43311, 43311, Kayah_Li]
- 110, // Range #182: [43312, 43347, Rejang]
- 110, // Range #183: [43359, 43359, Rejang]
- 18, // Range #184: [43360, 43388, Hangul]
- 78, // Range #185: [43392, 43469, Javanese]
- 78, // Range #186: [43472, 43487, Javanese]
- 28, // Range #187: [43488, 43518, Myanmar]
- 66, // Range #188: [43520, 43574, Cham]
- 66, // Range #189: [43584, 43615, Cham]
- 28, // Range #190: [43616, 43647, Myanmar]
- 127, // Range #191: [43648, 43714, Tai_Viet]
- 127, // Range #192: [43739, 43743, Tai_Viet]
- 115, // Range #193: [43744, 43766, Meetei_Mayek]
- 11, // Range #194: [43777, 43798, Ethiopic]
- 11, // Range #195: [43808, 43822, Ethiopic]
- 25, // Range #196: [43824, 43866, Latin]
- 25, // Range #197: [43868, 43876, Latin]
- 14, // Range #198: [43877, 43877, Greek]
- 25, // Range #199: [43878, 43881, Latin]
- 6, // Range #200: [43888, 43967, Cherokee]
- 115, // Range #201: [43968, 44025, Meetei_Mayek]
- 18, // Range #202: [44032, 55203, Hangul]
- 18, // Range #203: [55216, 55291, Hangul]
- 17, // Range #204: [63744, 64217, Han]
- 25, // Range #205: [64256, 64262, Latin]
- 3, // Range #206: [64275, 64279, Armenian]
- 19, // Range #207: [64285, 64335, Hebrew]
- 2, // Range #208: [64336, 64449, Arabic]
- 2, // Range #209: [64467, 64829, Arabic]
- 2, // Range #210: [64848, 64967, Arabic]
- 2, // Range #211: [65008, 65021, Arabic]
- 8, // Range #212: [65070, 65071, Cyrillic]
- 2, // Range #213: [65136, 65276, Arabic]
- 25, // Range #214: [65313, 65338, Latin]
- 25, // Range #215: [65345, 65370, Latin]
- 22, // Range #216: [65382, 65391, Katakana]
- 22, // Range #217: [65393, 65437, Katakana]
- 18, // Range #218: [65440, 65500, Hangul]
- 49, // Range #219: [65536, 65629, Linear_B]
- 49, // Range #220: [65664, 65786, Linear_B]
- 14, // Range #221: [65856, 65934, Greek]
- 14, // Range #222: [65952, 65952, Greek]
- 107, // Range #223: [66176, 66204, Lycian]
- 104, // Range #224: [66208, 66256, Carian]
- 30, // Range #225: [66304, 66339, Old_Italic]
- 30, // Range #226: [66349, 66351, Old_Italic]
- 13, // Range #227: [66352, 66378, Gothic]
- 89, // Range #228: [66384, 66426, Old_Permic]
- 53, // Range #229: [66432, 66463, Ugaritic]
- 61, // Range #230: [66464, 66517, Old_Persian]
- 9, // Range #231: [66560, 66639, Deseret]
- 51, // Range #232: [66640, 66687, Shavian]
- 50, // Range #233: [66688, 66729, Osmanya]
- 171, // Range #234: [66736, 66811, Osage]
- 136, // Range #235: [66816, 66855, Elbasan]
- 159, // Range #236: [66864, 66915, Caucasian_Albanian]
- 159, // Range #237: [66927, 66927, Caucasian_Albanian]
- 83, // Range #238: [67072, 67382, Linear_A]
- 83, // Range #239: [67392, 67413, Linear_A]
- 83, // Range #240: [67424, 67431, Linear_A]
- 47, // Range #241: [67584, 67647, Cypriot]
- 116, // Range #242: [67648, 67679, Imperial_Aramaic]
- 144, // Range #243: [67680, 67711, Palmyrene]
- 143, // Range #244: [67712, 67742, Nabataean]
- 143, // Range #245: [67751, 67759, Nabataean]
- 162, // Range #246: [67808, 67829, Hatran]
- 162, // Range #247: [67835, 67839, Hatran]
- 91, // Range #248: [67840, 67871, Phoenician]
- 108, // Range #249: [67872, 67897, Lydian]
- 108, // Range #250: [67903, 67903, Lydian]
- 86, // Range #251: [67968, 67999, Meroitic_Hieroglyphs]
- 141, // Range #252: [68000, 68095, Meroitic_Cursive]
- 57, // Range #253: [68096, 68102, Kharoshthi]
- 57, // Range #254: [68108, 68168, Kharoshthi]
- 57, // Range #255: [68176, 68184, Kharoshthi]
- 133, // Range #256: [68192, 68223, Old_South_Arabian]
- 142, // Range #257: [68224, 68255, Old_North_Arabian]
- 121, // Range #258: [68288, 68342, Manichaean]
- 117, // Range #259: [68352, 68415, Avestan]
- 125, // Range #260: [68416, 68447, Inscriptional_Parthian]
- 122, // Range #261: [68448, 68466, Inscriptional_Pahlavi]
- 122, // Range #262: [68472, 68479, Inscriptional_Pahlavi]
- 123, // Range #263: [68480, 68497, Psalter_Pahlavi]
- 123, // Range #264: [68505, 68508, Psalter_Pahlavi]
- 123, // Range #265: [68521, 68527, Psalter_Pahlavi]
- 88, // Range #266: [68608, 68680, Old_Turkic]
- 76, // Range #267: [68736, 68786, Old_Hungarian]
- 76, // Range #268: [68800, 68850, Old_Hungarian]
- 76, // Range #269: [68858, 68863, Old_Hungarian]
- 182, // Range #270: [68864, 68903, Hanifi_Rohingya]
- 182, // Range #271: [68912, 68921, Hanifi_Rohingya]
- 2, // Range #272: [69216, 69246, Arabic]
- 192, // Range #273: [69248, 69297, Yezidi]
- 184, // Range #274: [69376, 69415, Old_Sogdian]
- 183, // Range #275: [69424, 69465, Sogdian]
- 189, // Range #276: [69552, 69579, Chorasmian]
- 185, // Range #277: [69600, 69622, Elymaic]
- 65, // Range #278: [69632, 69743, Brahmi]
- 65, // Range #279: [69759, 69759, Brahmi]
- 120, // Range #280: [69760, 69825, Kaithi]
- 120, // Range #281: [69837, 69837, Kaithi]
- 152, // Range #282: [69840, 69864, Sora_Sompeng]
- 152, // Range #283: [69872, 69881, Sora_Sompeng]
- 118, // Range #284: [69888, 69959, Chakma]
- 160, // Range #285: [69968, 70006, Mahajani]
- 151, // Range #286: [70016, 70111, Sharada]
- 33, // Range #287: [70113, 70132, Sinhala]
- 157, // Range #288: [70144, 70206, Khojki]
- 164, // Range #289: [70272, 70313, Multani]
- 145, // Range #290: [70320, 70378, Khudawadi]
- 145, // Range #291: [70384, 70393, Khudawadi]
- 137, // Range #292: [70400, 70457, Grantha]
- 137, // Range #293: [70460, 70480, Grantha]
- 137, // Range #294: [70487, 70487, Grantha]
- 137, // Range #295: [70493, 70516, Grantha]
- 170, // Range #296: [70656, 70753, Newa]
- 158, // Range #297: [70784, 70855, Tirhuta]
- 158, // Range #298: [70864, 70873, Tirhuta]
- 166, // Range #299: [71040, 71133, Siddham]
- 163, // Range #300: [71168, 71236, Modi]
- 163, // Range #301: [71248, 71257, Modi]
- 27, // Range #302: [71264, 71276, Mongolian]
- 153, // Range #303: [71296, 71352, Takri]
- 153, // Range #304: [71360, 71369, Takri]
- 161, // Range #305: [71424, 71487, Ahom]
- 178, // Range #306: [71680, 71739, Dogra]
- 146, // Range #307: [71840, 71922, Warang_Citi]
- 146, // Range #308: [71935, 71935, Warang_Citi]
- 190, // Range #309: [71936, 72006, Dives_Akuru]
- 190, // Range #310: [72016, 72025, Dives_Akuru]
- 187, // Range #311: [72096, 72164, Nandinagari]
- 177, // Range #312: [72192, 72263, Zanabazar_Square]
- 176, // Range #313: [72272, 72354, Soyombo]
- 165, // Range #314: [72384, 72440, Pau_Cin_Hau]
- 168, // Range #315: [72704, 72773, Bhaiksuki]
- 168, // Range #316: [72784, 72812, Bhaiksuki]
- 169, // Range #317: [72816, 72886, Marchen]
- 175, // Range #318: [72960, 73031, Masaram_Gondi]
- 175, // Range #319: [73040, 73049, Masaram_Gondi]
- 179, // Range #320: [73056, 73112, Gunjala_Gondi]
- 179, // Range #321: [73120, 73129, Gunjala_Gondi]
- 180, // Range #322: [73440, 73464, Makasar]
- 131, // Range #323: [73648, 73648, Lisu]
- 35, // Range #324: [73664, 73713, Tamil]
- 35, // Range #325: [73727, 73727, Tamil]
- 101, // Range #326: [73728, 74649, Cuneiform]
- 101, // Range #327: [74752, 74868, Cuneiform]
- 101, // Range #328: [74880, 75075, Cuneiform]
- 71, // Range #329: [77824, 78904, Egyptian_Hieroglyphs]
- 156, // Range #330: [82944, 83526, Anatolian_Hieroglyphs]
- 130, // Range #331: [92160, 92728, Bamum]
- 149, // Range #332: [92736, 92783, Mro]
- 134, // Range #333: [92880, 92917, Bassa_Vah]
- 75, // Range #334: [92928, 92997, Pahawh_Hmong]
- 75, // Range #335: [93008, 93047, Pahawh_Hmong]
- 75, // Range #336: [93053, 93071, Pahawh_Hmong]
- 181, // Range #337: [93760, 93850, Medefaidrin]
- 92, // Range #338: [93952, 94087, Miao]
- 92, // Range #339: [94095, 94111, Miao]
- 154, // Range #340: [94176, 94176, Tangut]
- 150, // Range #341: [94177, 94177, Nushu]
- 191, // Range #342: [94180, 94180, Khitan_Small_Script]
- 17, // Range #343: [94192, 94193, Han]
- 154, // Range #344: [94208, 100343, Tangut]
- 154, // Range #345: [100352, 101119, Tangut]
- 191, // Range #346: [101120, 101589, Khitan_Small_Script]
- 154, // Range #347: [101632, 101640, Tangut]
- 22, // Range #348: [110592, 110592, Katakana]
- 20, // Range #349: [110593, 110878, Hiragana]
- 20, // Range #350: [110928, 110930, Hiragana]
- 22, // Range #351: [110948, 110951, Katakana]
- 150, // Range #352: [110960, 111355, Nushu]
- 135, // Range #353: [113664, 113770, Duployan]
- 135, // Range #354: [113776, 113800, Duployan]
- 135, // Range #355: [113808, 113823, Duployan]
- 14, // Range #356: [119296, 119365, Greek]
- 112, // Range #357: [120832, 121483, SignWriting]
- 112, // Range #358: [121499, 121519, SignWriting]
- 56, // Range #359: [122880, 122922, Glagolitic]
- 186, // Range #360: [123136, 123215, Nyiakeng_Puachue_Hmong]
- 188, // Range #361: [123584, 123641, Wancho]
- 188, // Range #362: [123647, 123647, Wancho]
- 140, // Range #363: [124928, 125142, Mende_Kikakui]
- 167, // Range #364: [125184, 125279, Adlam]
- 2, // Range #365: [126464, 126523, Arabic]
- 2, // Range #366: [126530, 126619, Arabic]
- 2, // Range #367: [126625, 126651, Arabic]
- 2, // Range #368: [126704, 126705, Arabic]
- 20, // Range #369: [127488, 127488, Hiragana]
- 17, // Range #370: [131072, 173789, Han]
- 17, // Range #371: [173824, 177972, Han]
- 17, // Range #372: [177984, 183969, Han]
- 17, // Range #373: [183984, 191456, Han]
- 17, // Range #374: [194560, 195101, Han]
- 17, // Range #375: [196608, 201546, Han]
+ 25, // Range #174: [42960, 42969, Latin]
+ 25, // Range #175: [42994, 43007, Latin]
+ 58, // Range #176: [43008, 43052, Syloti_Nagri]
+ 90, // Range #177: [43072, 43127, Phags_Pa]
+ 111, // Range #178: [43136, 43205, Saurashtra]
+ 111, // Range #179: [43214, 43225, Saurashtra]
+ 10, // Range #180: [43232, 43263, Devanagari]
+ 79, // Range #181: [43264, 43309, Kayah_Li]
+ 79, // Range #182: [43311, 43311, Kayah_Li]
+ 110, // Range #183: [43312, 43347, Rejang]
+ 110, // Range #184: [43359, 43359, Rejang]
+ 18, // Range #185: [43360, 43388, Hangul]
+ 78, // Range #186: [43392, 43469, Javanese]
+ 78, // Range #187: [43472, 43487, Javanese]
+ 28, // Range #188: [43488, 43518, Myanmar]
+ 66, // Range #189: [43520, 43574, Cham]
+ 66, // Range #190: [43584, 43615, Cham]
+ 28, // Range #191: [43616, 43647, Myanmar]
+ 127, // Range #192: [43648, 43714, Tai_Viet]
+ 127, // Range #193: [43739, 43743, Tai_Viet]
+ 115, // Range #194: [43744, 43766, Meetei_Mayek]
+ 11, // Range #195: [43777, 43798, Ethiopic]
+ 11, // Range #196: [43808, 43822, Ethiopic]
+ 25, // Range #197: [43824, 43866, Latin]
+ 25, // Range #198: [43868, 43876, Latin]
+ 14, // Range #199: [43877, 43877, Greek]
+ 25, // Range #200: [43878, 43881, Latin]
+ 6, // Range #201: [43888, 43967, Cherokee]
+ 115, // Range #202: [43968, 44025, Meetei_Mayek]
+ 18, // Range #203: [44032, 55203, Hangul]
+ 18, // Range #204: [55216, 55291, Hangul]
+ 17, // Range #205: [63744, 64217, Han]
+ 25, // Range #206: [64256, 64262, Latin]
+ 3, // Range #207: [64275, 64279, Armenian]
+ 19, // Range #208: [64285, 64335, Hebrew]
+ 2, // Range #209: [64336, 64450, Arabic]
+ 2, // Range #210: [64467, 64829, Arabic]
+ 2, // Range #211: [64832, 64967, Arabic]
+ 2, // Range #212: [64975, 64975, Arabic]
+ 2, // Range #213: [65008, 65023, Arabic]
+ 8, // Range #214: [65070, 65071, Cyrillic]
+ 2, // Range #215: [65136, 65276, Arabic]
+ 25, // Range #216: [65313, 65338, Latin]
+ 25, // Range #217: [65345, 65370, Latin]
+ 22, // Range #218: [65382, 65391, Katakana]
+ 22, // Range #219: [65393, 65437, Katakana]
+ 18, // Range #220: [65440, 65500, Hangul]
+ 49, // Range #221: [65536, 65629, Linear_B]
+ 49, // Range #222: [65664, 65786, Linear_B]
+ 14, // Range #223: [65856, 65934, Greek]
+ 14, // Range #224: [65952, 65952, Greek]
+ 107, // Range #225: [66176, 66204, Lycian]
+ 104, // Range #226: [66208, 66256, Carian]
+ 30, // Range #227: [66304, 66339, Old_Italic]
+ 30, // Range #228: [66349, 66351, Old_Italic]
+ 13, // Range #229: [66352, 66378, Gothic]
+ 89, // Range #230: [66384, 66426, Old_Permic]
+ 53, // Range #231: [66432, 66463, Ugaritic]
+ 61, // Range #232: [66464, 66517, Old_Persian]
+ 9, // Range #233: [66560, 66639, Deseret]
+ 51, // Range #234: [66640, 66687, Shavian]
+ 50, // Range #235: [66688, 66729, Osmanya]
+ 171, // Range #236: [66736, 66811, Osage]
+ 136, // Range #237: [66816, 66855, Elbasan]
+ 159, // Range #238: [66864, 66915, Caucasian_Albanian]
+ 159, // Range #239: [66927, 66927, Caucasian_Albanian]
+ 197, // Range #240: [66928, 67004, Vithkuqi]
+ 83, // Range #241: [67072, 67382, Linear_A]
+ 83, // Range #242: [67392, 67413, Linear_A]
+ 83, // Range #243: [67424, 67431, Linear_A]
+ 25, // Range #244: [67456, 67514, Latin]
+ 47, // Range #245: [67584, 67647, Cypriot]
+ 116, // Range #246: [67648, 67679, Imperial_Aramaic]
+ 144, // Range #247: [67680, 67711, Palmyrene]
+ 143, // Range #248: [67712, 67742, Nabataean]
+ 143, // Range #249: [67751, 67759, Nabataean]
+ 162, // Range #250: [67808, 67829, Hatran]
+ 162, // Range #251: [67835, 67839, Hatran]
+ 91, // Range #252: [67840, 67871, Phoenician]
+ 108, // Range #253: [67872, 67897, Lydian]
+ 108, // Range #254: [67903, 67903, Lydian]
+ 86, // Range #255: [67968, 67999, Meroitic_Hieroglyphs]
+ 141, // Range #256: [68000, 68095, Meroitic_Cursive]
+ 57, // Range #257: [68096, 68102, Kharoshthi]
+ 57, // Range #258: [68108, 68168, Kharoshthi]
+ 57, // Range #259: [68176, 68184, Kharoshthi]
+ 133, // Range #260: [68192, 68223, Old_South_Arabian]
+ 142, // Range #261: [68224, 68255, Old_North_Arabian]
+ 121, // Range #262: [68288, 68342, Manichaean]
+ 117, // Range #263: [68352, 68415, Avestan]
+ 125, // Range #264: [68416, 68447, Inscriptional_Parthian]
+ 122, // Range #265: [68448, 68466, Inscriptional_Pahlavi]
+ 122, // Range #266: [68472, 68479, Inscriptional_Pahlavi]
+ 123, // Range #267: [68480, 68497, Psalter_Pahlavi]
+ 123, // Range #268: [68505, 68508, Psalter_Pahlavi]
+ 123, // Range #269: [68521, 68527, Psalter_Pahlavi]
+ 88, // Range #270: [68608, 68680, Old_Turkic]
+ 76, // Range #271: [68736, 68786, Old_Hungarian]
+ 76, // Range #272: [68800, 68850, Old_Hungarian]
+ 76, // Range #273: [68858, 68863, Old_Hungarian]
+ 182, // Range #274: [68864, 68903, Hanifi_Rohingya]
+ 182, // Range #275: [68912, 68921, Hanifi_Rohingya]
+ 2, // Range #276: [69216, 69246, Arabic]
+ 192, // Range #277: [69248, 69297, Yezidi]
+ 184, // Range #278: [69376, 69415, Old_Sogdian]
+ 183, // Range #279: [69424, 69465, Sogdian]
+ 194, // Range #280: [69488, 69513, Old_Uyghur]
+ 189, // Range #281: [69552, 69579, Chorasmian]
+ 185, // Range #282: [69600, 69622, Elymaic]
+ 65, // Range #283: [69632, 69749, Brahmi]
+ 65, // Range #284: [69759, 69759, Brahmi]
+ 120, // Range #285: [69760, 69826, Kaithi]
+ 120, // Range #286: [69837, 69837, Kaithi]
+ 152, // Range #287: [69840, 69864, Sora_Sompeng]
+ 152, // Range #288: [69872, 69881, Sora_Sompeng]
+ 118, // Range #289: [69888, 69959, Chakma]
+ 160, // Range #290: [69968, 70006, Mahajani]
+ 151, // Range #291: [70016, 70111, Sharada]
+ 33, // Range #292: [70113, 70132, Sinhala]
+ 157, // Range #293: [70144, 70206, Khojki]
+ 164, // Range #294: [70272, 70313, Multani]
+ 145, // Range #295: [70320, 70378, Khudawadi]
+ 145, // Range #296: [70384, 70393, Khudawadi]
+ 137, // Range #297: [70400, 70457, Grantha]
+ 137, // Range #298: [70460, 70480, Grantha]
+ 137, // Range #299: [70487, 70487, Grantha]
+ 137, // Range #300: [70493, 70516, Grantha]
+ 170, // Range #301: [70656, 70753, Newa]
+ 158, // Range #302: [70784, 70855, Tirhuta]
+ 158, // Range #303: [70864, 70873, Tirhuta]
+ 166, // Range #304: [71040, 71133, Siddham]
+ 163, // Range #305: [71168, 71236, Modi]
+ 163, // Range #306: [71248, 71257, Modi]
+ 27, // Range #307: [71264, 71276, Mongolian]
+ 153, // Range #308: [71296, 71353, Takri]
+ 153, // Range #309: [71360, 71369, Takri]
+ 161, // Range #310: [71424, 71494, Ahom]
+ 178, // Range #311: [71680, 71739, Dogra]
+ 146, // Range #312: [71840, 71922, Warang_Citi]
+ 146, // Range #313: [71935, 71935, Warang_Citi]
+ 190, // Range #314: [71936, 72006, Dives_Akuru]
+ 190, // Range #315: [72016, 72025, Dives_Akuru]
+ 187, // Range #316: [72096, 72164, Nandinagari]
+ 177, // Range #317: [72192, 72263, Zanabazar_Square]
+ 176, // Range #318: [72272, 72354, Soyombo]
+ 40, // Range #319: [72368, 72383, Canadian_Aboriginal]
+ 165, // Range #320: [72384, 72440, Pau_Cin_Hau]
+ 168, // Range #321: [72704, 72773, Bhaiksuki]
+ 168, // Range #322: [72784, 72812, Bhaiksuki]
+ 169, // Range #323: [72816, 72886, Marchen]
+ 175, // Range #324: [72960, 73031, Masaram_Gondi]
+ 175, // Range #325: [73040, 73049, Masaram_Gondi]
+ 179, // Range #326: [73056, 73112, Gunjala_Gondi]
+ 179, // Range #327: [73120, 73129, Gunjala_Gondi]
+ 180, // Range #328: [73440, 73464, Makasar]
+ 131, // Range #329: [73648, 73648, Lisu]
+ 35, // Range #330: [73664, 73713, Tamil]
+ 35, // Range #331: [73727, 73727, Tamil]
+ 101, // Range #332: [73728, 74649, Cuneiform]
+ 101, // Range #333: [74752, 74868, Cuneiform]
+ 101, // Range #334: [74880, 75075, Cuneiform]
+ 193, // Range #335: [77712, 77810, Cypro_Minoan]
+ 71, // Range #336: [77824, 78904, Egyptian_Hieroglyphs]
+ 156, // Range #337: [82944, 83526, Anatolian_Hieroglyphs]
+ 130, // Range #338: [92160, 92728, Bamum]
+ 149, // Range #339: [92736, 92783, Mro]
+ 195, // Range #340: [92784, 92873, Tangsa]
+ 134, // Range #341: [92880, 92917, Bassa_Vah]
+ 75, // Range #342: [92928, 92997, Pahawh_Hmong]
+ 75, // Range #343: [93008, 93047, Pahawh_Hmong]
+ 75, // Range #344: [93053, 93071, Pahawh_Hmong]
+ 181, // Range #345: [93760, 93850, Medefaidrin]
+ 92, // Range #346: [93952, 94087, Miao]
+ 92, // Range #347: [94095, 94111, Miao]
+ 154, // Range #348: [94176, 94176, Tangut]
+ 150, // Range #349: [94177, 94177, Nushu]
+ 17, // Range #350: [94178, 94179, Han]
+ 191, // Range #351: [94180, 94180, Khitan_Small_Script]
+ 17, // Range #352: [94192, 94193, Han]
+ 154, // Range #353: [94208, 100343, Tangut]
+ 154, // Range #354: [100352, 101119, Tangut]
+ 191, // Range #355: [101120, 101589, Khitan_Small_Script]
+ 154, // Range #356: [101632, 101640, Tangut]
+ 22, // Range #357: [110576, 110592, Katakana]
+ 20, // Range #358: [110593, 110879, Hiragana]
+ 22, // Range #359: [110880, 110882, Katakana]
+ 20, // Range #360: [110928, 110930, Hiragana]
+ 22, // Range #361: [110948, 110951, Katakana]
+ 150, // Range #362: [110960, 111355, Nushu]
+ 135, // Range #363: [113664, 113770, Duployan]
+ 135, // Range #364: [113776, 113800, Duployan]
+ 135, // Range #365: [113808, 113823, Duployan]
+ 14, // Range #366: [119296, 119365, Greek]
+ 112, // Range #367: [120832, 121483, SignWriting]
+ 112, // Range #368: [121499, 121519, SignWriting]
+ 25, // Range #369: [122624, 122654, Latin]
+ 56, // Range #370: [122880, 122922, Glagolitic]
+ 186, // Range #371: [123136, 123215, Nyiakeng_Puachue_Hmong]
+ 196, // Range #372: [123536, 123566, Toto]
+ 188, // Range #373: [123584, 123641, Wancho]
+ 188, // Range #374: [123647, 123647, Wancho]
+ 11, // Range #375: [124896, 124926, Ethiopic]
+ 140, // Range #376: [124928, 125142, Mende_Kikakui]
+ 167, // Range #377: [125184, 125279, Adlam]
+ 2, // Range #378: [126464, 126523, Arabic]
+ 2, // Range #379: [126530, 126619, Arabic]
+ 2, // Range #380: [126625, 126651, Arabic]
+ 2, // Range #381: [126704, 126705, Arabic]
+ 20, // Range #382: [127488, 127488, Hiragana]
+ 17, // Range #383: [131072, 173791, Han]
+ 17, // Range #384: [173824, 177976, Han]
+ 17, // Range #385: [177984, 183969, Han]
+ 17, // Range #386: [183984, 191456, Han]
+ 17, // Range #387: [194560, 195101, Han]
+ 17, // Range #388: [196608, 201546, Han]
};
-const uint8 kMaxScript = 192;
+const uint8 kMaxScript = 197;
} // namespace approx_script_internal
} // namespace mobile