Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 94f47074 authored by Nishant Singh's avatar Nishant Singh Committed by Android (Google) Code Review
Browse files

Merge "Report data to ApplicationLocalesChanged atom."

parents c945d140 8b79e267
Loading
Loading
Loading
Loading
+53 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.locales;

import static android.os.Process.INVALID_UID;

import com.android.internal.util.FrameworkStatsLog;

/**
 * Holds data used to report the ApplicationLocalesChanged atom.
 */
public final class AppLocaleChangedAtomRecord {
    final int mCallingUid;
    int mTargetUid = INVALID_UID;
    String mNewLocales = "";
    String mPrevLocales = "";
    int mStatus = FrameworkStatsLog
            .APPLICATION_LOCALES_CHANGED__STATUS__STATUS_UNSPECIFIED;

    AppLocaleChangedAtomRecord(int callingUid) {
        this.mCallingUid = callingUid;
    }

    void setNewLocales(String newLocales) {
        this.mNewLocales = newLocales;
    }

    void setTargetUid(int targetUid) {
        this.mTargetUid = targetUid;
    }

    void setPrevLocales(String prevLocales) {
        this.mPrevLocales = prevLocales;
    }

    void setStatus(int status) {
        this.mStatus = status;
    }
}
+79 −28
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -148,9 +149,12 @@ public class LocaleManagerService extends SystemService {
     */
    public void setApplicationLocales(@NonNull String appPackageName, @UserIdInt int userId,
            @NonNull LocaleList locales) throws RemoteException, IllegalArgumentException {
        AppLocaleChangedAtomRecord atomRecordForMetrics = new
                AppLocaleChangedAtomRecord(Binder.getCallingUid());
        try {
            requireNonNull(appPackageName);
            requireNonNull(locales);

            atomRecordForMetrics.setNewLocales(locales.toLanguageTags());
            //Allow apps with INTERACT_ACROSS_USERS permission to set locales for different user.
            userId = mActivityManagerInternal.handleIncomingUser(
                    Binder.getCallingPid(), Binder.getCallingUid(), userId,
@@ -161,27 +165,36 @@ public class LocaleManagerService extends SystemService {
            // 1.) A normal, non-privileged app setting its own locale.
            // 2.) A privileged system service setting locales of another package.
            // The least privileged case is a normal app performing a set, so check that first and
        // set locales if the package name is owned by the app. Next, check if the caller has the
        // necessary permission and set locales.
        boolean isCallerOwner = isPackageOwnedByCaller(appPackageName, userId);
            // set locales if the package name is owned by the app. Next, check if the caller has
            // the necessary permission and set locales.
            boolean isCallerOwner = isPackageOwnedByCaller(appPackageName, userId,
                    atomRecordForMetrics);
            if (!isCallerOwner) {
            enforceChangeConfigurationPermission();
                enforceChangeConfigurationPermission(atomRecordForMetrics);
            }

            final long token = Binder.clearCallingIdentity();
            try {
            setApplicationLocalesUnchecked(appPackageName, userId, locales);
                setApplicationLocalesUnchecked(appPackageName, userId, locales,
                        atomRecordForMetrics);
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        } finally {
            logMetric(atomRecordForMetrics);
        }
    }

    private void setApplicationLocalesUnchecked(@NonNull String appPackageName,
            @UserIdInt int userId, @NonNull LocaleList locales) {
            @UserIdInt int userId, @NonNull LocaleList locales,
            @NonNull AppLocaleChangedAtomRecord atomRecordForMetrics) {
        if (DEBUG) {
            Slog.d(TAG, "setApplicationLocales: setting locales for package " + appPackageName
                    + " and user " + userId);
        }

        atomRecordForMetrics.setPrevLocales(getApplicationLocalesUnchecked(appPackageName, userId)
                .toLanguageTags());
        final ActivityTaskManagerInternal.PackageConfigurationUpdater updater =
                mActivityTaskManagerInternal.createPackageConfigurationUpdater(appPackageName,
                        userId);
@@ -194,6 +207,11 @@ public class LocaleManagerService extends SystemService {
            notifyRegisteredReceivers(appPackageName, userId, locales);

            mBackupHelper.notifyBackupManager();
            atomRecordForMetrics.setStatus(
                    FrameworkStatsLog.APPLICATION_LOCALES_CHANGED__STATUS__CONFIG_COMMITTED);
        } else {
            atomRecordForMetrics.setStatus(FrameworkStatsLog
                    .APPLICATION_LOCALES_CHANGED__STATUS__CONFIG_UNCOMMITTED);
        }
    }

@@ -258,27 +276,50 @@ public class LocaleManagerService extends SystemService {
                        | Intent.FLAG_RECEIVER_FOREGROUND);
    }

    /**
     * Same as {@link LocaleManagerService#isPackageOwnedByCaller(String, int,
     * AppLocaleChangedAtomRecord)}, but for methods that do not log locale atom.
     */
    private boolean isPackageOwnedByCaller(String appPackageName, int userId) {
        return isPackageOwnedByCaller(appPackageName, userId, /* atomRecordForMetrics= */null);
    }

    /**
     * Checks if the package is owned by the calling app or not for the given user id.
     *
     * @throws IllegalArgumentException if package not found for given userid
     */
    private boolean isPackageOwnedByCaller(String appPackageName, int userId) {
    private boolean isPackageOwnedByCaller(String appPackageName, int userId,
            @Nullable AppLocaleChangedAtomRecord atomRecordForMetrics) {
        final int uid = mPackageManagerInternal
                .getPackageUid(appPackageName, /* flags */ 0, userId);
        if (uid < 0) {
            Slog.w(TAG, "Unknown package " + appPackageName + " for user " + userId);
            if (atomRecordForMetrics != null) {
                atomRecordForMetrics.setStatus(FrameworkStatsLog
                        .APPLICATION_LOCALES_CHANGED__STATUS__FAILURE_INVALID_TARGET_PACKAGE);
            }
            throw new IllegalArgumentException("Unknown package: " + appPackageName
                    + " for user " + userId);
        }
        if (atomRecordForMetrics != null) {
            atomRecordForMetrics.setTargetUid(uid);
        }
        //Once valid package found, ignore the userId part for validating package ownership
        //as apps with INTERACT_ACROSS_USERS permission could be changing locale for different user.
        return UserHandle.isSameApp(Binder.getCallingUid(), uid);
    }

    private void enforceChangeConfigurationPermission() {
    private void enforceChangeConfigurationPermission(@NonNull AppLocaleChangedAtomRecord
            atomRecordForMetrics) {
        try {
            mContext.enforceCallingOrSelfPermission(
                    android.Manifest.permission.CHANGE_CONFIGURATION, "setApplicationLocales");
        } catch (SecurityException e) {
            atomRecordForMetrics.setStatus(FrameworkStatsLog
                    .APPLICATION_LOCALES_CHANGED__STATUS__FAILURE_PERMISSION_ABSENT);
            throw e;
        }
    }

    /**
@@ -312,6 +353,7 @@ public class LocaleManagerService extends SystemService {
        }
    }

    @NonNull
    private LocaleList getApplicationLocalesUnchecked(@NonNull String appPackageName,
            @UserIdInt int userId) {
        if (DEBUG) {
@@ -345,4 +387,13 @@ public class LocaleManagerService extends SystemService {
        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
        // TODO(b/201766221): Implement when there is state.
    }

    private void logMetric(@NonNull AppLocaleChangedAtomRecord atomRecordForMetrics) {
        FrameworkStatsLog.write(FrameworkStatsLog.APPLICATION_LOCALES_CHANGED,
                atomRecordForMetrics.mCallingUid,
                atomRecordForMetrics.mTargetUid,
                atomRecordForMetrics.mNewLocales,
                atomRecordForMetrics.mPrevLocales,
                atomRecordForMetrics.mStatus);
    }
}