From 1f35a414e3d7a064cbf15d47627506d68d6cfced Mon Sep 17 00:00:00 2001 From: Josh Hou Date: Thu, 23 Mar 2023 11:59:54 +0000 Subject: [PATCH] Log data to ApplicationLocalesChanged atom Log the caller who changes the per-app locales Bug: 272741428 Test: atest ApplicationLocalesChangedAtomTests Change-Id: Ic96092330f51cb6e14c973d5c65a5274e206b795 --- .../locales/AppLocaleChangedAtomRecord.java | 28 ++++++++++++--- .../locales/LocaleManagerBackupHelper.java | 8 +++-- .../server/locales/LocaleManagerService.java | 29 ++++++++++----- .../LocaleManagerBackupRestoreTest.java | 36 ++++++++++++------- .../locales/LocaleManagerServiceTest.java | 19 ++++++---- 5 files changed, 87 insertions(+), 33 deletions(-) diff --git a/services/core/java/com/android/server/locales/AppLocaleChangedAtomRecord.java b/services/core/java/com/android/server/locales/AppLocaleChangedAtomRecord.java index 2be2ef8c35af5..7a70db22106ef 100644 --- a/services/core/java/com/android/server/locales/AppLocaleChangedAtomRecord.java +++ b/services/core/java/com/android/server/locales/AppLocaleChangedAtomRecord.java @@ -20,24 +20,32 @@ import static android.os.Process.INVALID_UID; import com.android.internal.util.FrameworkStatsLog; +import java.util.Locale; + /** * Holds data used to report the ApplicationLocalesChanged atom. */ public final class AppLocaleChangedAtomRecord { + private static final String DEFAULT_PREFIX = "default-"; final int mCallingUid; int mTargetUid = INVALID_UID; - String mNewLocales = ""; - String mPrevLocales = ""; + String mNewLocales = DEFAULT_PREFIX; + String mPrevLocales = DEFAULT_PREFIX; int mStatus = FrameworkStatsLog .APPLICATION_LOCALES_CHANGED__STATUS__STATUS_UNSPECIFIED; int mCaller = FrameworkStatsLog .APPLICATION_LOCALES_CHANGED__CALLER__CALLER_UNKNOWN; AppLocaleChangedAtomRecord(int callingUid) { this.mCallingUid = callingUid; + Locale defaultLocale = Locale.getDefault(); + if (defaultLocale != null) { + this.mNewLocales = DEFAULT_PREFIX + defaultLocale.toLanguageTag(); + this.mPrevLocales = DEFAULT_PREFIX + defaultLocale.toLanguageTag(); + } } void setNewLocales(String newLocales) { - this.mNewLocales = newLocales; + this.mNewLocales = convertEmptyLocales(newLocales); } void setTargetUid(int targetUid) { @@ -45,7 +53,7 @@ public final class AppLocaleChangedAtomRecord { } void setPrevLocales(String prevLocales) { - this.mPrevLocales = prevLocales; + this.mPrevLocales = convertEmptyLocales(prevLocales); } void setStatus(int status) { @@ -55,4 +63,16 @@ public final class AppLocaleChangedAtomRecord { void setCaller(int caller) { this.mCaller = caller; } + + private String convertEmptyLocales(String locales) { + String target = locales; + if ("".equals(locales)) { + Locale defaultLocale = Locale.getDefault(); + if (defaultLocale != null) { + target = DEFAULT_PREFIX + defaultLocale.toLanguageTag(); + } + } + + return target; + } } diff --git a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java index 6cd2ed41e94c5..0049213cbf559 100644 --- a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java +++ b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java @@ -44,6 +44,7 @@ import android.util.SparseArray; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.XmlUtils; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; @@ -377,7 +378,8 @@ class LocaleManagerBackupHelper { // Restore the locale immediately try { mLocaleManagerService.setApplicationLocales(pkgName, userId, - LocaleList.forLanguageTags(localesInfo.mLocales), localesInfo.mSetFromDelegate); + LocaleList.forLanguageTags(localesInfo.mLocales), localesInfo.mSetFromDelegate, + FrameworkStatsLog.APPLICATION_LOCALES_CHANGED__CALLER__CALLER_BACKUP_RESTORE); if (DEBUG) { Slog.d(TAG, "Restored locales=" + localesInfo.mLocales + " fromDelegate=" + localesInfo.mSetFromDelegate + " for package=" + pkgName); @@ -662,7 +664,9 @@ class LocaleManagerBackupHelper { try { LocaleConfig localeConfig = new LocaleConfig( mContext.createPackageContextAsUser(packageName, 0, UserHandle.of(userId))); - mLocaleManagerService.removeUnsupportedAppLocales(packageName, userId, localeConfig); + mLocaleManagerService.removeUnsupportedAppLocales(packageName, userId, localeConfig, + FrameworkStatsLog + .APPLICATION_LOCALES_CHANGED__CALLER__CALLER_APP_UPDATE_LOCALES_CHANGE); } catch (PackageManager.NameNotFoundException e) { Slog.e(TAG, "Can not found the package name : " + packageName + " / " + e); } diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java index e3a555bd2f6a1..43e346a5bfa3f 100644 --- a/services/core/java/com/android/server/locales/LocaleManagerService.java +++ b/services/core/java/com/android/server/locales/LocaleManagerService.java @@ -182,8 +182,11 @@ public class LocaleManagerService extends SystemService { @Override public void setApplicationLocales(@NonNull String appPackageName, @UserIdInt int userId, @NonNull LocaleList locales, boolean fromDelegate) throws RemoteException { + int caller = fromDelegate + ? FrameworkStatsLog.APPLICATION_LOCALES_CHANGED__CALLER__CALLER_DELEGATE + : FrameworkStatsLog.APPLICATION_LOCALES_CHANGED__CALLER__CALLER_APPS; LocaleManagerService.this.setApplicationLocales(appPackageName, userId, locales, - fromDelegate); + fromDelegate, caller); } @Override @@ -226,13 +229,14 @@ public class LocaleManagerService extends SystemService { * Sets the current UI locales for a specified app. */ public void setApplicationLocales(@NonNull String appPackageName, @UserIdInt int userId, - @NonNull LocaleList locales, boolean fromDelegate) + @NonNull LocaleList locales, boolean fromDelegate, int caller) throws RemoteException, IllegalArgumentException { AppLocaleChangedAtomRecord atomRecordForMetrics = new AppLocaleChangedAtomRecord(Binder.getCallingUid()); try { requireNonNull(appPackageName); requireNonNull(locales); + atomRecordForMetrics.setCaller(caller); atomRecordForMetrics.setNewLocales(locales.toLanguageTags()); //Allow apps with INTERACT_ACROSS_USERS permission to set locales for different user. userId = mActivityManagerInternal.handleIncomingUser( @@ -273,8 +277,8 @@ public class LocaleManagerService extends SystemService { + " and user " + userId); } - atomRecordForMetrics.setPrevLocales(getApplicationLocalesUnchecked(appPackageName, userId) - .toLanguageTags()); + atomRecordForMetrics.setPrevLocales( + getApplicationLocalesUnchecked(appPackageName, userId).toLanguageTags()); final ActivityTaskManagerInternal.PackageConfigurationUpdater updater = mActivityTaskManagerInternal.createPackageConfigurationUpdater(appPackageName, userId); @@ -619,7 +623,10 @@ public class LocaleManagerService extends SystemService { Slog.d(TAG, "remove the override LocaleConfig"); file.delete(); } - removeUnsupportedAppLocales(appPackageName, userId, resLocaleConfig); + removeUnsupportedAppLocales(appPackageName, userId, resLocaleConfig, + FrameworkStatsLog + .APPLICATION_LOCALES_CHANGED__CALLER__CALLER_DYNAMIC_LOCALES_CHANGE + ); atomRecord.setOverrideRemoved(true); atomRecord.setStatus(FrameworkStatsLog .APP_SUPPORTED_LOCALES_CHANGED__STATUS__SUCCESS); @@ -661,7 +668,10 @@ public class LocaleManagerService extends SystemService { } atomicFile.finishWrite(stream); // Clear per-app locales if they are not in the override LocaleConfig. - removeUnsupportedAppLocales(appPackageName, userId, overrideLocaleConfig); + removeUnsupportedAppLocales(appPackageName, userId, overrideLocaleConfig, + FrameworkStatsLog + .APPLICATION_LOCALES_CHANGED__CALLER__CALLER_DYNAMIC_LOCALES_CHANGE + ); if (overrideLocaleConfig.isSameLocaleConfig(resLocaleConfig)) { Slog.d(TAG, "setOverrideLocaleConfig, same as the app's LocaleConfig"); atomRecord.setSameAsResConfig(true); @@ -678,9 +688,12 @@ public class LocaleManagerService extends SystemService { /** * Checks if the per-app locales are in the LocaleConfig. Per-app locales missing from the * LocaleConfig will be removed. + * + *

Note: Check whether to remove the per-app locales when the app is upgraded or + * the LocaleConfig is overridden. */ void removeUnsupportedAppLocales(String appPackageName, int userId, - LocaleConfig localeConfig) { + LocaleConfig localeConfig, int caller) { LocaleList appLocales = getApplicationLocalesUnchecked(appPackageName, userId); // Remove the per-app locales from the locale list if they don't exist in the LocaleConfig. boolean resetAppLocales = false; @@ -707,7 +720,7 @@ public class LocaleManagerService extends SystemService { try { setApplicationLocales(appPackageName, userId, new LocaleList(newAppLocales.toArray(locales)), - mBackupHelper.areLocalesSetFromDelegate(userId, appPackageName)); + mBackupHelper.areLocalesSetFromDelegate(userId, appPackageName), caller); } catch (RemoteException | IllegalArgumentException e) { Slog.e(TAG, "Could not set locales for " + appPackageName, e); } diff --git a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java index 13371cce5fb58..40ecaf1770a9b 100644 --- a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java +++ b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java @@ -54,6 +54,7 @@ import android.util.Xml; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.internal.content.PackageMonitor; +import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.XmlUtils; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; @@ -264,7 +265,8 @@ public class LocaleManagerBackupRestoreTest { // Locales were restored verify(mMockLocaleManagerService, times(1)).setApplicationLocales(DEFAULT_PACKAGE_NAME, - DEFAULT_USER_ID, DEFAULT_LOCALES, false); + DEFAULT_USER_ID, DEFAULT_LOCALES, false, FrameworkStatsLog + .APPLICATION_LOCALES_CHANGED__CALLER__CALLER_BACKUP_RESTORE); checkStageDataDoesNotExist(DEFAULT_USER_ID); } @@ -280,7 +282,8 @@ public class LocaleManagerBackupRestoreTest { // Locales were restored verify(mMockLocaleManagerService, times(1)).setApplicationLocales(DEFAULT_PACKAGE_NAME, - DEFAULT_USER_ID, DEFAULT_LOCALES, false); + DEFAULT_USER_ID, DEFAULT_LOCALES, false, FrameworkStatsLog + .APPLICATION_LOCALES_CHANGED__CALLER__CALLER_BACKUP_RESTORE); checkStageDataDoesNotExist(DEFAULT_USER_ID); mBackupHelper.persistLocalesModificationInfo(DEFAULT_USER_ID, DEFAULT_PACKAGE_NAME, false, @@ -303,7 +306,8 @@ public class LocaleManagerBackupRestoreTest { // Locales were restored verify(mMockLocaleManagerService, times(1)).setApplicationLocales(DEFAULT_PACKAGE_NAME, - DEFAULT_USER_ID, DEFAULT_LOCALES, true); + DEFAULT_USER_ID, DEFAULT_LOCALES, true, FrameworkStatsLog + .APPLICATION_LOCALES_CHANGED__CALLER__CALLER_BACKUP_RESTORE); checkStageDataDoesNotExist(DEFAULT_USER_ID); mBackupHelper.persistLocalesModificationInfo(DEFAULT_USER_ID, DEFAULT_PACKAGE_NAME, true, @@ -327,7 +331,8 @@ public class LocaleManagerBackupRestoreTest { // Locales were restored verify(mMockLocaleManagerService, times(1)).setApplicationLocales( - DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID, DEFAULT_LOCALES, true); + DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID, DEFAULT_LOCALES, true, + FrameworkStatsLog.APPLICATION_LOCALES_CHANGED__CALLER__CALLER_BACKUP_RESTORE); checkStageDataDoesNotExist(DEFAULT_USER_ID); mBackupHelper.persistLocalesModificationInfo(DEFAULT_USER_ID, DEFAULT_PACKAGE_NAME, true, @@ -369,7 +374,8 @@ public class LocaleManagerBackupRestoreTest { mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID); verify(mMockLocaleManagerService, times(1)).setApplicationLocales(pkgNameA, DEFAULT_USER_ID, - LocaleList.forLanguageTags(langTagsA), true); + LocaleList.forLanguageTags(langTagsA), true, FrameworkStatsLog + .APPLICATION_LOCALES_CHANGED__CALLER__CALLER_BACKUP_RESTORE); pkgLocalesMap.remove(pkgNameA); @@ -422,11 +428,12 @@ public class LocaleManagerBackupRestoreTest { // Restore locales only for myAppB. verify(mMockLocaleManagerService, times(0)).setApplicationLocales(eq(pkgNameA), anyInt(), - any(), anyBoolean()); + any(), anyBoolean(), anyInt()); verify(mMockLocaleManagerService, times(1)).setApplicationLocales(pkgNameB, DEFAULT_USER_ID, - LocaleList.forLanguageTags(langTagsB), true); + LocaleList.forLanguageTags(langTagsB), true, FrameworkStatsLog + .APPLICATION_LOCALES_CHANGED__CALLER__CALLER_BACKUP_RESTORE); verify(mMockLocaleManagerService, times(0)).setApplicationLocales(eq(pkgNameC), anyInt(), - any(), anyBoolean()); + any(), anyBoolean(), anyInt()); // App C is staged. pkgLocalesMap.remove(pkgNameA); @@ -484,7 +491,8 @@ public class LocaleManagerBackupRestoreTest { mPackageMonitor.onPackageAdded(pkgNameA, DEFAULT_UID); verify(mMockLocaleManagerService, times(1)).setApplicationLocales(pkgNameA, DEFAULT_USER_ID, - LocaleList.forLanguageTags(langTagsA), false); + LocaleList.forLanguageTags(langTagsA), false, FrameworkStatsLog + .APPLICATION_LOCALES_CHANGED__CALLER__CALLER_BACKUP_RESTORE); mBackupHelper.persistLocalesModificationInfo(DEFAULT_USER_ID, pkgNameA, false, false); @@ -499,7 +507,8 @@ public class LocaleManagerBackupRestoreTest { mPackageMonitor.onPackageAdded(pkgNameB, DEFAULT_UID); verify(mMockLocaleManagerService, times(1)).setApplicationLocales(pkgNameB, DEFAULT_USER_ID, - LocaleList.forLanguageTags(langTagsB), true); + LocaleList.forLanguageTags(langTagsB), true, FrameworkStatsLog + .APPLICATION_LOCALES_CHANGED__CALLER__CALLER_BACKUP_RESTORE); mBackupHelper.persistLocalesModificationInfo(DEFAULT_USER_ID, pkgNameB, true, false); @@ -606,7 +615,8 @@ public class LocaleManagerBackupRestoreTest { mPackageMonitor.onPackageAdded(pkgNameA, DEFAULT_UID); verify(mMockLocaleManagerService, times(1)).setApplicationLocales( - pkgNameA, DEFAULT_USER_ID, LocaleList.forLanguageTags(langTagsA), false); + pkgNameA, DEFAULT_USER_ID, LocaleList.forLanguageTags(langTagsA), false, + FrameworkStatsLog.APPLICATION_LOCALES_CHANGED__CALLER__CALLER_BACKUP_RESTORE); pkgLocalesMap.remove(pkgNameA); @@ -620,7 +630,7 @@ public class LocaleManagerBackupRestoreTest { mPackageMonitor.onPackageAdded(pkgNameB, DEFAULT_UID); verify(mMockLocaleManagerService, times(0)).setApplicationLocales(eq(pkgNameB), anyInt(), - any(), anyBoolean()); + any(), anyBoolean(), anyInt()); checkStageDataDoesNotExist(DEFAULT_USER_ID); } @@ -734,7 +744,7 @@ public class LocaleManagerBackupRestoreTest { */ private void verifyNothingRestored() throws Exception { verify(mMockLocaleManagerService, times(0)).setApplicationLocales(anyString(), anyInt(), - any(), anyBoolean()); + any(), anyBoolean(), anyInt()); } private static void verifyPayloadForAppLocales(Map expectedPkgLocalesMap, diff --git a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java index 07fda309f03ee..550204b993230 100644 --- a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java @@ -52,6 +52,7 @@ import androidx.test.InstrumentationRegistry; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.internal.content.PackageMonitor; +import com.android.internal.util.FrameworkStatsLog; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal.PackageConfig; @@ -136,7 +137,8 @@ public class LocaleManagerServiceTest { try { mLocaleManagerService.setApplicationLocales(DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID, - LocaleList.getEmptyLocaleList(), false); + LocaleList.getEmptyLocaleList(), false, FrameworkStatsLog + .APPLICATION_LOCALES_CHANGED__CALLER__CALLER_APPS); fail("Expected SecurityException"); } finally { verify(mMockContext).enforceCallingOrSelfPermission( @@ -151,7 +153,8 @@ public class LocaleManagerServiceTest { public void testSetApplicationLocales_nullPackageName_fails() throws Exception { try { mLocaleManagerService.setApplicationLocales(/* appPackageName = */ null, - DEFAULT_USER_ID, LocaleList.getEmptyLocaleList(), false); + DEFAULT_USER_ID, LocaleList.getEmptyLocaleList(), false, + FrameworkStatsLog.APPLICATION_LOCALES_CHANGED__CALLER__CALLER_APPS); fail("Expected NullPointerException"); } finally { verify(mMockBackupHelper, times(0)).notifyBackupManager(); @@ -165,7 +168,8 @@ public class LocaleManagerServiceTest { try { mLocaleManagerService.setApplicationLocales(DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID, - /* locales = */ null, false); + /* locales = */ null, false, FrameworkStatsLog + .APPLICATION_LOCALES_CHANGED__CALLER__CALLER_APPS); fail("Expected NullPointerException"); } finally { verify(mMockBackupHelper, times(0)).notifyBackupManager(); @@ -183,7 +187,8 @@ public class LocaleManagerServiceTest { setUpPassingPermissionCheckFor(Manifest.permission.CHANGE_CONFIGURATION); mLocaleManagerService.setApplicationLocales(DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID, - DEFAULT_LOCALES, true); + DEFAULT_LOCALES, true, FrameworkStatsLog + .APPLICATION_LOCALES_CHANGED__CALLER__CALLER_DELEGATE); assertEquals(DEFAULT_LOCALES, mFakePackageConfigurationUpdater.getStoredLocales()); verify(mMockBackupHelper, times(1)).notifyBackupManager(); @@ -196,7 +201,8 @@ public class LocaleManagerServiceTest { .when(mMockPackageManager).getPackageUidAsUser(anyString(), any(), anyInt()); mLocaleManagerService.setApplicationLocales(DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID, - DEFAULT_LOCALES, false); + DEFAULT_LOCALES, false, FrameworkStatsLog + .APPLICATION_LOCALES_CHANGED__CALLER__CALLER_APPS); assertEquals(DEFAULT_LOCALES, mFakePackageConfigurationUpdater.getStoredLocales()); verify(mMockBackupHelper, times(1)).notifyBackupManager(); @@ -208,7 +214,8 @@ public class LocaleManagerServiceTest { .when(mMockPackageManager).getPackageUidAsUser(anyString(), any(), anyInt()); try { mLocaleManagerService.setApplicationLocales(DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID, - LocaleList.getEmptyLocaleList(), false); + LocaleList.getEmptyLocaleList(), false, FrameworkStatsLog + .APPLICATION_LOCALES_CHANGED__CALLER__CALLER_APPS); fail("Expected IllegalArgumentException"); } finally { assertNoLocalesStored(mFakePackageConfigurationUpdater.getStoredLocales());