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

Commit 8cdef4cb authored by Jeff Davidson's avatar Jeff Davidson Committed by Android (Google) Code Review
Browse files

Merge changes from topic 'carrier-app-race' into nyc-dev

* changes:
  Test disabling carrier apps with null TelephonyManager.
  Move CarrierAppUtils to frameworks/base/telephony.
parents d04d67e4 b548a8c8
Loading
Loading
Loading
Loading
+0 −211
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2015 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.internal.telephony;

import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.RemoteException;
import android.telephony.TelephonyManager;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;
import java.util.List;

/**
 * Utilities for handling carrier applications.
 * @hide
 */
public final class CarrierAppUtils {
    private static final String TAG = "CarrierAppUtils";

    private static final boolean DEBUG = false; // STOPSHIP if true

    private CarrierAppUtils() {}

    /**
     * Handle preinstalled carrier apps which should be disabled until a matching SIM is inserted.
     *
     * Evaluates the list of applications in config_disabledUntilUsedPreinstalledCarrierApps. We
     * want to disable each such application which is present on the system image until the user
     * inserts a SIM which causes that application to gain carrier privilege (indicating a "match"),
     * without interfering with the user if they opt to enable/disable the app explicitly.
     *
     * So, for each such app, we either disable until used IFF the app is not carrier privileged AND
     * in the default state (e.g. not explicitly DISABLED/DISABLED_BY_USER/ENABLED), or we enable if
     * the app is carrier privileged and in either the default state or DISABLED_UNTIL_USED.
     *
     * When enabling a carrier app we also grant it default permissions.
     *
     * This method is idempotent and is safe to be called at any time; it should be called once at
     * system startup prior to any application running, as well as any time the set of carrier
     * privileged apps may have changed.
     */
    public synchronized static void disableCarrierAppsUntilPrivileged(String callingPackage,
            IPackageManager packageManager, TelephonyManager telephonyManager, int userId) {
        if (DEBUG) {
            Slog.d(TAG, "disableCarrierAppsUntilPrivileged");
        }
        String[] systemCarrierAppsDisabledUntilUsed = Resources.getSystem().getStringArray(
                com.android.internal.R.array.config_disabledUntilUsedPreinstalledCarrierApps);
        disableCarrierAppsUntilPrivileged(callingPackage, packageManager, telephonyManager, userId,
                systemCarrierAppsDisabledUntilUsed);
    }

    // Must be public b/c framework unit tests can't access package-private methods.
    @VisibleForTesting
    public static void disableCarrierAppsUntilPrivileged(String callingPackage,
            IPackageManager packageManager, TelephonyManager telephonyManager, int userId,
            String[] systemCarrierAppsDisabledUntilUsed) {
        List<ApplicationInfo> candidates = getDefaultCarrierAppCandidatesHelper(packageManager,
                userId, systemCarrierAppsDisabledUntilUsed);
        if (candidates == null || candidates.isEmpty()) {
            return;
        }

        List<String> enabledCarrierPackages = new ArrayList<>();

        try {
            for (ApplicationInfo ai : candidates) {
                String packageName = ai.packageName;
                boolean hasPrivileges =
                        telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName) ==
                                TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;

                // Only update enabled state for the app on /system. Once it has been updated we
                // shouldn't touch it.
                if (!ai.isUpdatedSystemApp()) {
                    if (hasPrivileges
                            && (ai.enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
                            || ai.enabledSetting ==
                                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) {
                        Slog.i(TAG, "Update state(" + packageName + "): ENABLED for user "
                                + userId);
                        packageManager.setApplicationEnabledSetting(packageName,
                                PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                                PackageManager.DONT_KILL_APP, userId, callingPackage);
                    } else if (!hasPrivileges
                            && ai.enabledSetting ==
                                    PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
                        Slog.i(TAG, "Update state(" + packageName
                                + "): DISABLED_UNTIL_USED for user " + userId);
                        packageManager.setApplicationEnabledSetting(packageName,
                                PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, 0,
                                userId, callingPackage);
                    }
                }

                // Always re-grant default permissions to carrier apps w/ privileges.
                if (hasPrivileges) {
                    enabledCarrierPackages.add(ai.packageName);
                }
            }

            if (!enabledCarrierPackages.isEmpty()) {
                // Since we enabled at least one app, ensure we grant default permissions to those
                // apps.
                String[] packageNames = new String[enabledCarrierPackages.size()];
                enabledCarrierPackages.toArray(packageNames);
                packageManager.grantDefaultPermissionsToEnabledCarrierApps(packageNames, userId);
            }
        } catch (RemoteException e) {
            Slog.w(TAG, "Could not reach PackageManager", e);
        }
    }

    /**
     * Returns the list of "default" carrier apps.
     *
     * This is the subset of apps returned by
     * {@link #getDefaultCarrierAppCandidates(IPackageManager, int)} which currently have carrier
     * privileges per the SIM(s) inserted in the device.
     */
    public static List<ApplicationInfo> getDefaultCarrierApps(IPackageManager packageManager,
            TelephonyManager telephonyManager, int userId) {
        // Get all system apps from the default list.
        List<ApplicationInfo> candidates = getDefaultCarrierAppCandidates(packageManager, userId);
        if (candidates == null || candidates.isEmpty()) {
            return null;
        }

        // Filter out apps without carrier privileges.
        // Iterate from the end to avoid creating an Iterator object and because we will be removing
        // elements from the list as we pass through it.
        for (int i = candidates.size() - 1; i >= 0; i--) {
            ApplicationInfo ai = candidates.get(i);
            String packageName = ai.packageName;
            boolean hasPrivileges =
                    telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName) ==
                            TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
            if (!hasPrivileges) {
                candidates.remove(i);
            }
        }

        return candidates;
    }

    /**
     * Returns the list of "default" carrier app candidates.
     *
     * These are the apps subject to the hiding/showing logic in
     * {@link CarrierAppUtils#disableCarrierAppsUntilPrivileged(String, IPackageManager,
     * TelephonyManager, int)}, as well as the apps which should have default permissions granted,
     * when a matching SIM is inserted.
     *
     * Whether or not the app is actually considered a default app depends on whether the app has
     * carrier privileges as determined by the SIMs in the device.
     */
    public static List<ApplicationInfo> getDefaultCarrierAppCandidates(
            IPackageManager packageManager, int userId) {
        String[] systemCarrierAppsDisabledUntilUsed = Resources.getSystem().getStringArray(
                com.android.internal.R.array.config_disabledUntilUsedPreinstalledCarrierApps);
        return getDefaultCarrierAppCandidatesHelper(packageManager, userId,
                systemCarrierAppsDisabledUntilUsed);
    }

    private static List<ApplicationInfo> getDefaultCarrierAppCandidatesHelper(
            IPackageManager packageManager, int userId,
            String[] systemCarrierAppsDisabledUntilUsed) {
        if (systemCarrierAppsDisabledUntilUsed == null
                || systemCarrierAppsDisabledUntilUsed.length == 0) {
            return null;
        }
        List<ApplicationInfo> apps = null;
        try {
            apps = new ArrayList<>(systemCarrierAppsDisabledUntilUsed.length);
            for (String packageName : systemCarrierAppsDisabledUntilUsed) {
                ApplicationInfo ai = packageManager.getApplicationInfo(packageName,
                        PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, userId);
                if (ai == null) {
                    // No app found for packageName
                    continue;
                }
                if (!ai.isSystemApp()) {
                    continue;
                }
                apps.add(ai);
            }
        } catch (RemoteException e) {
            Slog.w(TAG, "Could not reach PackageManager", e);
        }
        return apps;
    }
}
+116 −0
Original line number Original line Diff line number Diff line
@@ -233,6 +233,26 @@ public class CarrierAppUtilsTest extends InstrumentationTestCase {
                        Mockito.any(String[].class), Mockito.anyInt());
                        Mockito.any(String[].class), Mockito.anyInt());
    }
    }


    /** Telephony is not initialized, and app was disabled by the user - should do nothing. */
    @Test @SmallTest
    public void testDisableCarrierAppsUntilPrivileged_NullPrivileges_DisabledUser()
            throws Exception {
        ApplicationInfo appInfo = new ApplicationInfo();
        appInfo.packageName = CARRIER_APP;
        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
        appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
        Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
        CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                null /* telephonyManager */, USER_ID, CARRIER_APPS);
        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
                Mockito.anyString());
        Mockito.verify(mPackageManager, Mockito.never())
                .grantDefaultPermissionsToEnabledCarrierApps(
                        Mockito.any(String[].class), Mockito.anyInt());
    }

    /** Configured app has no privileges, and was disabled - should do nothing. */
    /** Configured app has no privileges, and was disabled - should do nothing. */
    @Test @SmallTest
    @Test @SmallTest
    public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_Disabled() throws Exception {
    public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_Disabled() throws Exception {
@@ -254,6 +274,25 @@ public class CarrierAppUtilsTest extends InstrumentationTestCase {
                        Mockito.any(String[].class), Mockito.anyInt());
                        Mockito.any(String[].class), Mockito.anyInt());
    }
    }


    /** Telephony is not initialized, and app was disabled - should do nothing. */
    @Test @SmallTest
    public void testDisableCarrierAppsUntilPrivileged_NullPrivileges_Disabled() throws Exception {
        ApplicationInfo appInfo = new ApplicationInfo();
        appInfo.packageName = CARRIER_APP;
        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
        appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
        Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
        CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                null /* telephonyManager */, USER_ID, CARRIER_APPS);
        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
                Mockito.anyString());
        Mockito.verify(mPackageManager, Mockito.never())
                .grantDefaultPermissionsToEnabledCarrierApps(
                        Mockito.any(String[].class), Mockito.anyInt());
    }

    /** Configured app has no privileges, and is explicitly enabled - should do nothing. */
    /** Configured app has no privileges, and is explicitly enabled - should do nothing. */
    @Test @SmallTest
    @Test @SmallTest
    public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_Enabled() throws Exception {
    public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_Enabled() throws Exception {
@@ -275,6 +314,25 @@ public class CarrierAppUtilsTest extends InstrumentationTestCase {
                        Mockito.any(String[].class), Mockito.anyInt());
                        Mockito.any(String[].class), Mockito.anyInt());
    }
    }


    /** Telephony is not initialized, and app is explicitly enabled - should do nothing. */
    @Test @SmallTest
    public void testDisableCarrierAppsUntilPrivileged_NullPrivileges_Enabled() throws Exception {
        ApplicationInfo appInfo = new ApplicationInfo();
        appInfo.packageName = CARRIER_APP;
        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
        appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
        Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
        CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                null /* telephonyManager */, USER_ID, CARRIER_APPS);
        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
                Mockito.anyString());
        Mockito.verify(mPackageManager, Mockito.never())
                .grantDefaultPermissionsToEnabledCarrierApps(
                        Mockito.any(String[].class), Mockito.anyInt());
    }

    /** Configured /data app has no privileges - should do nothing. */
    /** Configured /data app has no privileges - should do nothing. */
    @Test @SmallTest
    @Test @SmallTest
    public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_UpdatedApp() throws Exception {
    public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_UpdatedApp() throws Exception {
@@ -296,6 +354,25 @@ public class CarrierAppUtilsTest extends InstrumentationTestCase {
                        Mockito.any(String[].class), Mockito.anyInt());
                        Mockito.any(String[].class), Mockito.anyInt());
    }
    }


    /** Telephony is not initialized and app is in /data - should do nothing. */
    @Test @SmallTest
    public void testDisableCarrierAppsUntilPrivileged_NullPrivileges_UpdatedApp() throws Exception {
        ApplicationInfo appInfo = new ApplicationInfo();
        appInfo.packageName = CARRIER_APP;
        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
        appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
        Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
        CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                null /* telephonyManager */, USER_ID, CARRIER_APPS);
        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
                Mockito.anyString());
        Mockito.verify(mPackageManager, Mockito.never())
                .grantDefaultPermissionsToEnabledCarrierApps(
                        Mockito.any(String[].class), Mockito.anyInt());
    }

    /** Configured app has no privileges, and is in the default state - should disable until use. */
    /** Configured app has no privileges, and is in the default state - should disable until use. */
    @Test @SmallTest
    @Test @SmallTest
    public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_Default() throws Exception {
    public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_Default() throws Exception {
@@ -317,6 +394,25 @@ public class CarrierAppUtilsTest extends InstrumentationTestCase {
                        Mockito.any(String[].class), Mockito.anyInt());
                        Mockito.any(String[].class), Mockito.anyInt());
    }
    }


    /** Telephony is not initialized, and app is in the default state - should disable until use. */
    @Test @SmallTest
    public void testDisableCarrierAppsUntilPrivileged_NullPrivileges_Default() throws Exception {
        ApplicationInfo appInfo = new ApplicationInfo();
        appInfo.packageName = CARRIER_APP;
        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
        appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
        Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
        CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                null /* telephonyManager */, USER_ID, CARRIER_APPS);
        Mockito.verify(mPackageManager).setApplicationEnabledSetting(
                CARRIER_APP, PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, 0, USER_ID,
                CALLING_PACKAGE);
        Mockito.verify(mPackageManager, Mockito.never())
                .grantDefaultPermissionsToEnabledCarrierApps(
                        Mockito.any(String[].class), Mockito.anyInt());
    }

    /** Configured app has no privileges, and is disabled until used - should do nothing. */
    /** Configured app has no privileges, and is disabled until used - should do nothing. */
    @Test @SmallTest
    @Test @SmallTest
    public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_DisabledUntilUsed()
    public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_DisabledUntilUsed()
@@ -338,5 +434,25 @@ public class CarrierAppUtilsTest extends InstrumentationTestCase {
                .grantDefaultPermissionsToEnabledCarrierApps(
                .grantDefaultPermissionsToEnabledCarrierApps(
                        Mockito.any(String[].class), Mockito.anyInt());
                        Mockito.any(String[].class), Mockito.anyInt());
    }
    }

    /** Telephony is not initialized, and app is disabled until used - should do nothing. */
    @Test @SmallTest
    public void testDisableCarrierAppsUntilPrivileged_NullPrivileges_DisabledUntilUsed()
            throws Exception {
        ApplicationInfo appInfo = new ApplicationInfo();
        appInfo.packageName = CARRIER_APP;
        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
        appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
        Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
        CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                null /* telephonyManager */, USER_ID, CARRIER_APPS);
        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
                Mockito.anyString());
        Mockito.verify(mPackageManager, Mockito.never())
                .grantDefaultPermissionsToEnabledCarrierApps(
                        Mockito.any(String[].class), Mockito.anyInt());
    }
}
}